[GOLD] PowerPC64 huge branch dynamic relocs
authorAlan Modra <amodra@gmail.com>
Fri, 23 Jun 2023 01:07:53 +0000 (10:37 +0930)
committerAlan Modra <amodra@gmail.com>
Fri, 23 Jun 2023 11:32:48 +0000 (21:02 +0930)
PowerPC64 gold and ld.bfd implement an indirect branch trampoline,
used when the destination of a branch exceeds a bounce through another
"b" instruction.  When generating PIEs or shared libraries, the
addresses need dynamic relocations.  This was implemented in gold
using a dedicated relocation section, but this means the relative
relocations for these addresses are not sorted properly with other
dynamic relative relocations: gold doesn't support merging relocation
sections, then sorting.  Instead we need to use a single .rela.dyn
section.

This is done by increasing the size of rela_dyn_ during do_relax to
account for needed dynamic relocations, delaying adding the actual
relocations until the end of relaxation once the layout has
stabilised.

* powerpc.cc (Target_powerpc): Add rela_dyn_size_;
(update_current_size): New function.
(Target_powerpc::do_relax): Capture the size of rela_dyn_ at
the start of relaxation.  Artifically increase its size during
relaxation to account for needed indirect branches, and add
those relocations at the end.
(Output_data_brlt_powerpc::rel_, reset_brlt_sizes),
(finalize_brlt_sizes, add_reloc, set_current_size): Delete.
(Target_powerpc::make_brlt_section): Don't make reloc section.

gold/powerpc.cc

index 5838b49e3418ae2f973cc51aa6be12dbd1fcf107..96fef7b8dde73a00cbb8640589f0dbb42a7cad68 100644 (file)
@@ -1763,6 +1763,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
   Branches branch_info_;
   Tocsave_loc tocsave_loc_;
 
+  off_t rela_dyn_size_;
+
   bool power10_relocs_;
   bool plt_thread_safe_;
   bool plt_localentry0_;
@@ -3687,6 +3689,18 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
   return ok;
 }
 
+// Helper for do_relax, avoiding checks that size, address and offset
+// are not set more than once.
+
+static inline void
+update_current_size(Output_section_data_build* od, off_t cur_size)
+{
+  od->reset_address_and_file_offset();
+  od->set_current_data_size(cur_size);
+  od->finalize_data_size();
+  od->output_section()->set_section_offsets_need_adjustment();
+}
+
 // Relaxation hook.  This is where we do stub generation.
 
 template<int size, bool big_endian>
@@ -3752,10 +3766,11 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
            }
        }
       this->plt_thread_safe_ = thread_safe;
-    }
 
-  if (pass == 1)
-    {
+      if (parameters->options().output_is_position_independent())
+       this->rela_dyn_size_
+         = this->rela_dyn_section(layout)->current_data_size();
+
       this->stub_group_size_ = parameters->options().stub_group_size();
       bool no_size_errors = true;
       if (this->stub_group_size_ == 1)
@@ -3868,7 +3883,15 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
   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);
+    {
+      update_current_size(this->brlt_section_, num_huge_branches * 16);
+      if (parameters->options().output_is_position_independent())
+       {
+         const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+         off_t cur = this->rela_dyn_size_ + num_huge_branches * reloc_size;
+         update_current_size(this->rela_dyn_, cur);
+       }
+    }
 
   for (typename Stub_tables::reverse_iterator p = this->stub_tables_.rbegin();
        p != this->stub_tables_.rend();
@@ -3937,15 +3960,21 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
       && parameters->options().output_is_position_independent())
     {
       // Fill in the BRLT relocs.
-      this->brlt_section_->reset_brlt_sizes();
+      this->rela_dyn_->reset_data_size();
+      this->rela_dyn_->set_current_data_size(this->rela_dyn_size_);
       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->rela_dyn_->add_relative(elfcpp::R_POWERPC_RELATIVE,
+                                       this->brlt_section_, p->second,
+                                       p->first);
        }
-      this->brlt_section_->finalize_brlt_sizes();
+      this->rela_dyn_->finalize_data_size();
+      const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+      gold_assert(this->rela_dyn_->data_size()
+                 == this->rela_dyn_size_ + num_huge_branches * reloc_size);
     }
 
   if (!again
@@ -4554,52 +4583,11 @@ class Output_data_brlt_powerpc : public Output_section_data_build
   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_data_brlt_powerpc(Target_powerpc<size, big_endian>* targ)
     : 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)
-      {
-       const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_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)
@@ -4617,8 +4605,6 @@ class Output_data_brlt_powerpc : public Output_section_data_build
   void
   do_write(Output_file*);
 
-  // The reloc section.
-  Reloc_section* rel_;
   Target_powerpc<size, big_endian>* targ_;
 };
 
@@ -4630,24 +4616,15 @@ 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 but must initialise at
          // runtime via dynamic relocations.
          this->rela_dyn_section(layout);
-         // FIXME: This reloc section won't have its relative relocs
-         // sorted properly among the other relative relocs in rela_dyn_
-         // but it must be a separate section due to needing to call
-         // reset_data_size().
-         brlt_rel = new Reloc_section(false);
-         if (this->rela_dyn_->output_section())
-           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);
+       = new Output_data_brlt_powerpc<size, big_endian>(this);
       if (this->plt_ && is_pic && this->plt_->output_section())
        this->plt_->output_section()
          ->add_output_section_data(this->brlt_section_);