This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Fixes to powerpc64 gold ELFv2 support


This gets me to a clean gold testsuite except for a couple of icf
failures I'm still investigating.  Some differences will be seen
between bfd ld and gold regarding ifunc use of the global entry plt
call stubs.  For instance, the ifuncmain4 test on gold emits three
global entry stubs and defines ifunc symbols on those stubs while bfd
ld emits no global entry stubs but uses more R_PPC64_IRELATIVE dynamic
relocations.  Which way is best is arguable..

	* powerpc.cc (Target_powerpc::glink_section): Provide non-const
	accessor.
	(Target_powerpc::Branch_info::make_stub): Make global entry stubs.
	Only call ppc64_local_entry_offset for 64-bit.  Restrict
	symval_for_branch lookup to ELFv1.
	(Stub_table::add_plt_call_entry): Use unsigned int off.
	(Output_data_glink::Address, invalid_address): New.
	(Output_data_glink::add_eh_frame): Move out of line.  Add
	support for ELFv2.
	(Output_data_glink::add_global_entry, find_global_entry,
	global_entry_address): New functions.
	(Output_data_glink::global_entry_stubs_, end_branch_table_,
	ge_size): New variables.
	(Output_data_glink::set_final_data_size): Add global entry
	stub sizing.
	(Output_data_glink::do_write): Write global entry stubs.
	(Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Add target
	parameter.  Return true for ELFv2.  Adjust callers.
	(Target_powerpc::Scan::local, global): Restrict opd lookup to
	ELFv1.  Similarly for ifunc and dynamic relocation processing
	specific to ELFv1.  Recognize that symbols are defined on
	their plt entries for ELFv2.
	(Target_powerpc::symval_for_branch): Assert if called for
	ELFv2 or ppc32.
	(Target_powerpc::Relocate::relocate): Use global entry plt
	stub for symbol value if such exists on ELFv2.
	(Target_powerpc::Relocate::relocate): Don't call
	symval_for_branch when ELFv2.  Do adjust for local entry
	offset when ELFv2.
	(Target_powerpc::do_dynsym_value): Set symbols to global entry
	plt stub for ELFv2.
	(Target_powerpc::do_plt_address_for_global): Similarly.

diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index ada4d0c..1aa4791 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -687,6 +687,13 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return this->glink_;
   }
 
+  Output_data_glink<size, big_endian>*
+  glink_section()
+  {
+    gold_assert(this->glink_ != NULL);
+    return this->glink_;
+  }
+
   bool has_glink() const
   { return this->glink_ != NULL; }
 
@@ -974,7 +981,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
     }
 
     static bool
-    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
+    reloc_needs_plt_for_ifunc(Target_powerpc<size, big_endian>* target,
+			      Sized_relobj_file<size, big_endian>* object,
 			      unsigned int r_type, bool report_err);
 
   private:
@@ -2530,20 +2538,29 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
       ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
       : this->object_->local_has_plt_offset(this->r_sym_))
     {
-      if (stub_table == NULL)
-	stub_table = this->object_->stub_table(this->shndx_);
-      if (stub_table == NULL)
+      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
 	{
-	  // This is a ref from a data section to an ifunc symbol.
-	  stub_table = ifunc_stub_table;
+	  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_);
 	}
-      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
     {
@@ -2590,7 +2607,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
 	  to = symtab->compute_final_value<size>(gsym, &status);
 	  if (status != Symbol_table::CFVS_OK)
 	    return;
-	  to += this->object_->ppc64_local_entry_offset(gsym);
+	  if (size == 64)
+	    to += this->object_->ppc64_local_entry_offset(gsym);
 	}
       else
 	{
@@ -2605,12 +2623,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
 	      || !symval.has_output_value())
 	    return;
 	  to = symval.value(this->object_, 0);
-	  to += this->object_->ppc64_local_entry_offset(this->r_sym_);
+	  if (size == 64)
+	    to += this->object_->ppc64_local_entry_offset(this->r_sym_);
 	}
       to += this->addend_;
       if (stub_table == NULL)
 	stub_table = this->object_->stub_table(this->shndx_);
-      if (size == 64 && is_branch_reloc(this->r_type_))
+      if (size == 64 && target->abiversion() < 2)
 	{
 	  unsigned int dest_shndx;
 	  to = target->symval_for_branch(symtab, to, gsym,
@@ -3036,6 +3055,7 @@ static const uint32_t ld_11_2		= 0xe9620000;
 static const uint32_t ld_11_11		= 0xe96b0000;
 static const uint32_t ld_12_2		= 0xe9820000;
 static const uint32_t ld_12_11		= 0xe98b0000;
+static const uint32_t ld_12_12		= 0xe98c0000;
 static const uint32_t lfd_0_1		= 0xc8010000;
 static const uint32_t li_0_0		= 0x38000000;
 static const uint32_t li_12_0		= 0x39800000;
@@ -3802,7 +3822,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
     Address addend)
 {
   Plt_stub_ent ent(object, gsym, r_type, addend);
-  Address off = this->plt_size_;
+  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)
@@ -3818,7 +3838,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
     Address addend)
 {
   Plt_stub_ent ent(object, locsym_index, r_type, addend);
-  Address off = this->plt_size_;
+  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)
@@ -3913,50 +3933,30 @@ 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)
+    : Output_section_data(16), targ_(targ), global_entry_stubs_(),
+      end_branch_table_(), ge_size_(0)
   { }
 
   void
-  add_eh_frame(Layout* layout)
-  {
-    if (!parameters->options().ld_generated_unwind_info())
-      return;
+  add_eh_frame(Layout* layout);
 
-    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));
-      }
+  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:
@@ -3975,10 +3975,76 @@ class Output_data_glink : public Output_section_data
 
   // 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));
+    }
+}
+
+template<int size, bool big_endian>
+void
+Output_data_glink<size, big_endian>::add_global_entry(const Symbol* gsym)
+{
+  std::pair<typename Global_entry_stub_entries::iterator, bool> p
+    = this->global_entry_stubs_.insert(std::make_pair(gsym, this->ge_size_));
+  if (p.second)
+    this->ge_size_ += 16;
+}
+
+template<int size, bool big_endian>
+typename Output_data_glink<size, big_endian>::Address
+Output_data_glink<size, big_endian>::find_global_entry(const Symbol* gsym) const
+{
+  typename Global_entry_stub_entries::const_iterator p
+    = this->global_entry_stubs_.find(gsym);
+  return p == this->global_entry_stubs_.end() ? invalid_address : p->second;
+}
+
+template<int size, bool big_endian>
+void
 Output_data_glink<size, big_endian>::set_final_data_size()
 {
   unsigned int count = this->targ_->plt_entry_count();
@@ -4008,6 +4074,9 @@ Output_data_glink<size, big_endian>::set_final_data_size()
 	    }
 	}
     }
+  this->end_branch_table_ = total;
+  total = (total + 15) & -16;
+  total += this->ge_size_;
 
   this->set_data_size(total);
 }
@@ -4341,64 +4410,101 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
 
   if (size == 64)
     {
-      // Write pltresolve stub.
-      p = oview;
-      Address after_bcl = this->address() + 16;
-      Address pltoff = plt_base - after_bcl;
-
-      elfcpp::Swap<64, big_endian>::writeval(p, pltoff),	p += 8;
-
-      if (this->targ_->abiversion() < 2)
-	{
-	  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, ld_2_11 + l(-16)),		p += 4;
-	  write_insn<big_endian>(p, mtlr_12),			p += 4;
-	  write_insn<big_endian>(p, add_11_2_11),		p += 4;
-	  write_insn<big_endian>(p, ld_12_11 + 0),		p += 4;
-	  write_insn<big_endian>(p, ld_2_11 + 8),		p += 4;
-	  write_insn<big_endian>(p, mtctr_12),			p += 4;
-	  write_insn<big_endian>(p, ld_11_11 + 16),		p += 4;
-	}
-      else
+      if (this->end_branch_table_ != 0)
 	{
-	  write_insn<big_endian>(p, mflr_0),			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, ld_2_11 + l(-16)),		p += 4;
-	  write_insn<big_endian>(p, mtlr_0),			p += 4;
-	  write_insn<big_endian>(p, sub_12_12_11),		p += 4;
-	  write_insn<big_endian>(p, add_11_2_11),		p += 4;
-	  write_insn<big_endian>(p, addi_0_12 + l(-48)),	p += 4;
-	  write_insn<big_endian>(p, ld_12_11 + 0),		p += 4;
-	  write_insn<big_endian>(p, srdi_0_0_2),		p += 4;
-	  write_insn<big_endian>(p, mtctr_12),			p += 4;
-	  write_insn<big_endian>(p, ld_11_11 + 8),		p += 4;
-	}
-      write_insn<big_endian>(p, bctr),				p += 4;
-      while (p < oview + this->pltresolve_size)
-	write_insn<big_endian>(p, nop), p += 4;
+	  // Write pltresolve stub.
+	  p = oview;
+	  Address after_bcl = this->address() + 16;
+	  Address pltoff = plt_base - after_bcl;
+
+	  elfcpp::Swap<64, big_endian>::writeval(p, pltoff),	p += 8;
 
-      // Write lazy link call stubs.
-      uint32_t indx = 0;
-      while (p < oview + oview_size)
-	{
 	  if (this->targ_->abiversion() < 2)
 	    {
-	      if (indx < 0x8000)
-		{
-		  write_insn<big_endian>(p, li_0_0 + indx),		p += 4;
-		}
-	      else
+	      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, ld_2_11 + l(-16)),	p += 4;
+	      write_insn<big_endian>(p, mtlr_12),		p += 4;
+	      write_insn<big_endian>(p, add_11_2_11),		p += 4;
+	      write_insn<big_endian>(p, ld_12_11 + 0),		p += 4;
+	      write_insn<big_endian>(p, ld_2_11 + 8),		p += 4;
+	      write_insn<big_endian>(p, mtctr_12),		p += 4;
+	      write_insn<big_endian>(p, ld_11_11 + 16),		p += 4;
+	    }
+	  else
+	    {
+	      write_insn<big_endian>(p, mflr_0),		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, ld_2_11 + l(-16)),	p += 4;
+	      write_insn<big_endian>(p, mtlr_0),		p += 4;
+	      write_insn<big_endian>(p, sub_12_12_11),		p += 4;
+	      write_insn<big_endian>(p, add_11_2_11),		p += 4;
+	      write_insn<big_endian>(p, addi_0_12 + l(-48)),	p += 4;
+	      write_insn<big_endian>(p, ld_12_11 + 0),		p += 4;
+	      write_insn<big_endian>(p, srdi_0_0_2),		p += 4;
+	      write_insn<big_endian>(p, mtctr_12),		p += 4;
+	      write_insn<big_endian>(p, ld_11_11 + 8),		p += 4;
+	    }
+	  write_insn<big_endian>(p, bctr),			p += 4;
+	  while (p < oview + this->pltresolve_size)
+	    write_insn<big_endian>(p, nop), p += 4;
+
+	  // Write lazy link call stubs.
+	  uint32_t indx = 0;
+	  while (p < oview + this->end_branch_table_)
+	    {
+	      if (this->targ_->abiversion() < 2)
 		{
-		  write_insn<big_endian>(p, lis_0_0 + hi(indx)),	p += 4;
-		  write_insn<big_endian>(p, ori_0_0_0 + l(indx)),	p += 4;
+		  if (indx < 0x8000)
+		    {
+		      write_insn<big_endian>(p, li_0_0 + indx),		p += 4;
+		    }
+		  else
+		    {
+		      write_insn<big_endian>(p, lis_0_0 + hi(indx)),	p += 4;
+		      write_insn<big_endian>(p, ori_0_0_0 + l(indx)),	p += 4;
+		    }
 		}
+	      uint32_t branch_off = 8 - (p - oview);
+	      write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)),	p += 4;
+	      indx++;
 	    }
-	  uint32_t branch_off = 8 - (p - oview);
-	  write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)),	p += 4;
-	  indx++;
+	}
+
+      Address plt_base = this->targ_->plt_section()->address();
+      Address iplt_base = invalid_address;
+      unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
+      Address global_entry_base = this->address() + global_entry_off;
+      typename Global_entry_stub_entries::const_iterator ge;
+      for (ge = this->global_entry_stubs_.begin();
+	   ge != this->global_entry_stubs_.end();
+	   ++ge)
+	{
+	  p = oview + global_entry_off + ge->second;
+	  Address plt_addr = ge->first->plt_offset();
+	  if (ge->first->type() == elfcpp::STT_GNU_IFUNC
+	      && ge->first->can_use_relative_reloc(false))
+	    {
+	      if (iplt_base == invalid_address)
+		iplt_base = this->targ_->iplt_section()->address();
+	      plt_addr += iplt_base;
+	    }
+	  else
+	    plt_addr += plt_base;
+	  Address my_addr = global_entry_base + ge->second;
+	  Address off = plt_addr - my_addr;
+
+	  if (off + 0x80008000 > 0xffffffff || (off & 3) != 0)
+	    gold_error(_("%s: linkage table error against `%s'"),
+		       ge->first->object()->name().c_str(),
+		       ge->first->demangled_name().c_str());
+
+	  write_insn<big_endian>(p, addis_12_12 + ha(off)),	p += 4;
+	  write_insn<big_endian>(p, ld_12_12 + l(off)),		p += 4;
+	  write_insn<big_endian>(p, mtctr_12),			p += 4;
+	  write_insn<big_endian>(p, bctr);
 	}
     }
   else
@@ -5119,13 +5225,15 @@ Target_powerpc<size, big_endian>::Scan::check_non_pic(Relobj* object,
 template<int size, bool big_endian>
 bool
 Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
+     Target_powerpc<size, big_endian>* target,
      Sized_relobj_file<size, big_endian>* object,
      unsigned int r_type,
      bool report_err)
 {
   // In non-pic code any reference will resolve to the plt call stub
   // for the ifunc symbol.
-  if (size == 32 && !parameters->options().output_is_position_independent())
+  if ((size == 32 || target->abiversion() >= 2)
+      && !parameters->options().output_is_position_independent())
     return true;
 
   switch (r_type)
@@ -5232,7 +5340,7 @@ Target_powerpc<size, big_endian>::Scan::local(
 
   // A local STT_GNU_IFUNC symbol may require a PLT entry.
   bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
-  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true))
     {
       unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
@@ -5257,6 +5365,7 @@ Target_powerpc<size, big_endian>::Scan::local(
 	  {
 	    Address off = reloc.get_r_offset();
 	    if (size == 64
+		&& target->abiversion() < 2
 		&& data_shndx == ppc_object->opd_shndx()
 		&& ppc_object->get_opd_discard(off - 8))
 	      break;
@@ -5297,7 +5406,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       // executable), we need to create a dynamic relocation for
       // this location.
       if (parameters->options().output_is_position_independent()
-	  || (size == 64 && is_ifunc))
+	  || (size == 64 && is_ifunc && target->abiversion() < 2))
 	{
 	  Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
 							     is_ifunc);
@@ -5389,7 +5498,8 @@ Target_powerpc<size, big_endian>::Scan::local(
 
 	if (!parameters->options().output_is_position_independent())
 	  {
-	    if (size == 32 && is_ifunc)
+	    if ((size == 32 && is_ifunc)
+		|| (size == 64 && target->abiversion() >= 2))
 	      got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
 	    else
 	      got->add_local(object, r_sym, GOT_TYPE_STANDARD);
@@ -5587,12 +5697,14 @@ Target_powerpc<size, big_endian>::Scan::global(
 
   // A STT_GNU_IFUNC symbol may require a PLT entry.
   bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
-  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
+  bool pushed_ifunc = false;
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true))
     {
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
 			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
 			  reloc.get_r_addend());
       target->make_plt_entry(symtab, layout, gsym);
+      pushed_ifunc = true;
     }
 
   switch (r_type)
@@ -5632,6 +5744,7 @@ Target_powerpc<size, big_endian>::Scan::global(
 
     case elfcpp::R_PPC64_ADDR64:
       if (size == 64
+	  && target->abiversion() < 2
 	  && data_shndx == ppc_object->opd_shndx()
 	  && (gsym->is_defined_in_discarded_section()
 	      || gsym->object() != object))
@@ -5664,7 +5777,19 @@ Target_powerpc<size, big_endian>::Scan::global(
 	// Make a PLT entry if necessary.
 	if (gsym->needs_plt_entry())
 	  {
-	    if (!is_ifunc)
+	    // Since this is not a PC-relative relocation, we may be
+	    // taking the address of a function. In that case we need to
+	    // set the entry in the dynamic symbol table to the address of
+	    // the PLT call stub.
+	    bool need_ifunc_plt = false;
+	    if ((size == 32 || target->abiversion() >= 2)
+		&& gsym->is_from_dynobj()
+		&& !parameters->options().output_is_position_independent())
+	      {
+		gsym->set_needs_dynsym_value();
+		need_ifunc_plt = true;
+	      }
+	    if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt))
 	      {
 		target->push_branch(ppc_object, data_shndx,
 				    reloc.get_r_offset(), r_type,
@@ -5672,31 +5797,27 @@ Target_powerpc<size, big_endian>::Scan::global(
 				    reloc.get_r_addend());
 		target->make_plt_entry(symtab, layout, gsym);
 	      }
-	    // Since this is not a PC-relative relocation, we may be
-	    // taking the address of a function. In that case we need to
-	    // set the entry in the dynamic symbol table to the address of
-	    // the PLT call stub.
-	    if (size == 32
-		&& gsym->is_from_dynobj()
-		&& !parameters->options().output_is_position_independent())
-	      gsym->set_needs_dynsym_value();
 	  }
 	// Make a dynamic relocation if necessary.
 	if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type, target))
-	    || (size == 64 && is_ifunc))
+	    || (size == 64 && is_ifunc && target->abiversion() < 2))
 	  {
 	    if (gsym->may_need_copy_reloc())
 	      {
 		target->copy_reloc(symtab, layout, object,
 				   data_shndx, output_section, gsym, reloc);
 	      }
-	    else if ((size == 32
-		      && r_type == elfcpp::R_POWERPC_ADDR32
+	    else if ((((size == 32
+			&& r_type == elfcpp::R_POWERPC_ADDR32)
+		       || (size == 64
+			   && r_type == elfcpp::R_PPC64_ADDR64
+			   && target->abiversion() >= 2))
 		      && gsym->can_use_relative_reloc(false)
 		      && !(gsym->visibility() == elfcpp::STV_PROTECTED
 			   && parameters->options().shared()))
 		     || (size == 64
 			 && r_type == elfcpp::R_PPC64_ADDR64
+			 && target->abiversion() < 2
 			 && (gsym->can_use_relative_reloc(false)
 			     || data_shndx == ppc_object->opd_shndx())))
 	      {
@@ -5822,7 +5943,8 @@ Target_powerpc<size, big_endian>::Scan::global(
 	got = target->got_section(symtab, layout);
 	if (gsym->final_value_is_known())
 	  {
-	    if (size == 32 && is_ifunc)
+	    if ((size == 32 && is_ifunc)
+		|| (size == 64 && target->abiversion() >= 2))
 	      got->add_global_plt(gsym, GOT_TYPE_STANDARD);
 	    else
 	      got->add_global(gsym, GOT_TYPE_STANDARD);
@@ -5838,7 +5960,8 @@ Target_powerpc<size, big_endian>::Scan::global(
 	      = target->rela_dyn_section(symtab, layout, is_ifunc);
 
 	    if (gsym->can_use_relative_reloc(false)
-		&& !(size == 32
+		&& !((size == 32
+		      || target->abiversion() >= 2)
 		     && gsym->visibility() == elfcpp::STV_PROTECTED
 		     && parameters->options().shared()))
 	      {
@@ -6438,9 +6561,9 @@ Target_powerpc<size, big_endian>::symval_for_branch(
     Powerpc_relobj<size, big_endian>* object,
     unsigned int *dest_shndx)
 {
+  if (size == 32 || this->abiversion() >= 2)
+    gold_unreachable();
   *dest_shndx = 0;
-  if (size == 32)
-    return value;
 
   // If the symbol is defined in an opd section, ie. is a function
   // descriptor, use the function descriptor code entry address
@@ -6522,26 +6645,39 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
        : object->local_has_plt_offset(r_sym))
       && (!psymval->is_ifunc_symbol()
-	  || Scan::reloc_needs_plt_for_ifunc(object, r_type, false)))
+	  || Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
     {
-      Stub_table<size, big_endian>* stub_table
-	= object->stub_table(relinfo->data_shndx);
-      if (stub_table == NULL)
+      if (size == 64
+	  && gsym != NULL
+	  && target->abiversion() >= 2
+	  && !parameters->options().output_is_position_independent()
+	  && !is_branch_reloc(r_type))
 	{
-	  // This is a ref from a data section to an ifunc symbol.
-	  if (target->stub_tables().size() != 0)
-	    stub_table = target->stub_tables()[0];
+	  unsigned int off = target->glink_section()->find_global_entry(gsym);
+	  gold_assert(off != (unsigned int)-1);
+	  value = target->glink_section()->global_entry_address() + off;
 	}
-      gold_assert(stub_table != NULL);
-      Address off;
-      if (gsym != NULL)
-	off = stub_table->find_plt_call_entry(object, gsym, r_type,
-					      rela.get_r_addend());
       else
-	off = stub_table->find_plt_call_entry(object, r_sym, r_type,
-					      rela.get_r_addend());
-      gold_assert(off != invalid_address);
-      value = stub_table->stub_address() + off;
+	{
+	  Stub_table<size, big_endian>* stub_table
+	    = object->stub_table(relinfo->data_shndx);
+	  if (stub_table == NULL)
+	    {
+	      // This is a ref from a data section to an ifunc symbol.
+	      if (target->stub_tables().size() != 0)
+		stub_table = target->stub_tables()[0];
+	    }
+	  gold_assert(stub_table != NULL);
+	  Address off;
+	  if (gsym != NULL)
+	    off = stub_table->find_plt_call_entry(object, gsym, r_type,
+						  rela.get_r_addend());
+	  else
+	    off = stub_table->find_plt_call_entry(object, r_sym, r_type,
+						  rela.get_r_addend());
+	  gold_assert(off != invalid_address);
+	  value = stub_table->stub_address() + off;
+	}
       has_plt_value = true;
     }
 
@@ -6616,11 +6752,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
 	      if (gsym->source() == Symbol::FROM_OBJECT
 		  && gsym->object() == object)
 		{
-		  Address addend = rela.get_r_addend();
-		  unsigned int dest_shndx;
-		  Address opdent = psymval->value(object, addend);
-		  code = target->symval_for_branch(relinfo->symtab, opdent,
-						   gsym, object, &dest_shndx);
+		  unsigned int dest_shndx = 0;
+		  if (target->abiversion() < 2)
+		    {
+		      Address addend = rela.get_r_addend();
+		      Address opdent = psymval->value(object, addend);
+		      code = target->symval_for_branch(relinfo->symtab,
+						       opdent, gsym, object,
+						       &dest_shndx);
+		    }
 		  bool is_ordinary;
 		  if (dest_shndx == 0)
 		    dest_shndx = gsym->shndx(&is_ordinary);
@@ -6881,13 +7021,19 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       if (r_type != elfcpp::R_PPC_PLTREL24)
 	addend = rela.get_r_addend();
       value = psymval->value(object, addend);
-      if (gsym != NULL)
-	value += object->ppc64_local_entry_offset(gsym);
-      else
-	value += object->ppc64_local_entry_offset(r_sym);
       if (size == 64 && is_branch_reloc(r_type))
-	value = target->symval_for_branch(relinfo->symtab, value,
-					  gsym, object, &dest_shndx);
+	{
+	  if (target->abiversion() >= 2)
+	    {
+	      if (gsym != NULL)
+		value += object->ppc64_local_entry_offset(gsym);
+	      else
+		value += object->ppc64_local_entry_offset(r_sym);
+	    }
+	  else
+	    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
@@ -7846,6 +7992,12 @@ Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
 	    return (*p)->stub_address() + off;
 	}
     }
+  else if (this->abiversion() >= 2)
+    {
+      unsigned int off = this->glink_section()->find_global_entry(gsym);
+      if (off != (unsigned int)-1)
+	return this->glink_section()->global_entry_address() + off;
+    }
   gold_unreachable();
 }
 
@@ -7890,6 +8042,12 @@ Target_powerpc<size, big_endian>::do_plt_address_for_global(
 	    return (*p)->stub_address() + off;
 	}
     }
+  else if (this->abiversion() >= 2)
+    {
+      unsigned int off = this->glink_section()->find_global_entry(gsym);
+      if (off != (unsigned int)-1)
+	return this->glink_section()->global_entry_address() + off;
+    }
   gold_unreachable();
 }
 
@@ -7987,6 +8145,9 @@ Target_selector_powerpc<64, false> target_selector_ppc64le;
 template<int size, bool big_endian>
 const int Output_data_glink<size, big_endian>::pltresolve_size;
 template<int size, bool big_endian>
+const typename Output_data_glink<size, big_endian>::Address
+  Output_data_glink<size, big_endian>::invalid_address;
+template<int size, bool big_endian>
 const typename Stub_table<size, big_endian>::Address
   Stub_table<size, big_endian>::invalid_address;
 template<int size, bool big_endian>

-- 
Alan Modra
Australia Development Lab, IBM


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]