[GOLD] Edit PowerPC64 ELFv2 function entry code
authorAlan Modra <amodra@gmail.com>
Tue, 8 Dec 2015 23:48:44 +0000 (10:18 +1030)
committerAlan Modra <amodra@gmail.com>
Wed, 9 Dec 2015 00:06:43 +0000 (10:36 +1030)
In an fixed position executable, the entry code does not need to be
PIC and can thus lose a dependency on r12.

* powerpc.cc (Target_powerpc::Relocate::relocate): Edit ELFv2
entry code.
(Target_powerpc::relocate_relocs): Edit relocs to suit.

gold/ChangeLog
gold/powerpc.cc

index be004cd..824296b 100644 (file)
@@ -1,5 +1,11 @@
 2015-12-09  Alan Modra  <amodra@gmail.com>
 
+       * powerpc.cc (Target_powerpc::Relocate::relocate): Edit ELFv2
+       entry code.
+       (Target_powerpc::relocate_relocs): Edit relocs to suit.
+
+2015-12-09  Alan Modra  <amodra@gmail.com>
+
        * object.h (struct Relocate_info): Add "rr".
        * reloc.h (Relocatable_relocs::set_strategy): New accessor.
        * reloc.cc (Sized_relobj_file::do_relocate_sections): Init
index f16faf7..71507fe 100644 (file)
@@ -7027,6 +7027,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
 
   typedef Powerpc_relocate_functions<size, big_endian> Reloc;
   typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
+  typedef typename Reloc_types<elfcpp::SHT_RELA,
+                              size, big_endian>::Reloc Reltype;
   Powerpc_relobj<size, big_endian>* const object
     = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
   Address value = 0;
@@ -7700,6 +7702,55 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                }
            }
          break;
+
+       case elfcpp::R_POWERPC_REL16_LO:
+         // If we are generating a non-PIC executable, edit
+         //    0:      addis 2,12,.TOC.-0b@ha
+         //            addi 2,2,.TOC.-0b@l
+         // used by ELFv2 global entry points to set up r2, to
+         //            lis 2,.TOC.@ha
+         //            addi 2,2,.TOC.@l
+         // if .TOC. is in range.  */
+         if (value + address - 4 + 0x80008000 <= 0xffffffff
+             && relnum != 0
+             && preloc != NULL
+             && target->abiversion() >= 2
+             && !parameters->options().output_is_position_independent()
+             && gsym != NULL
+             && strcmp(gsym->name(), ".TOC.") == 0)
+           {
+             const int reloc_size
+               = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+             Reltype prev_rela(preloc - reloc_size);
+             if ((prev_rela.get_r_info()
+                  == elfcpp::elf_r_info<size>(r_sym,
+                                              elfcpp::R_POWERPC_REL16_HA))
+                 && prev_rela.get_r_offset() + 4 == rela.get_r_offset()
+                 && prev_rela.get_r_addend() + 4 == rela.get_r_addend())
+               {
+                 Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+                 Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview - 1);
+                 Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview);
+
+                 if ((insn1 & 0xffff0000) == addis_2_12
+                     && (insn2 & 0xffff0000) == addi_2_2)
+                   {
+                     insn1 = lis_2 + ha(value + address - 4);
+                     elfcpp::Swap<32, big_endian>::writeval(iview - 1, insn1);
+                     insn2 = addi_2_2 + l(value + address - 4);
+                     elfcpp::Swap<32, big_endian>::writeval(iview, insn2);
+                     if (relinfo->rr)
+                       {
+                         relinfo->rr->set_strategy(relnum - 1,
+                                                   Relocatable_relocs::RELOC_SPECIAL);
+                         relinfo->rr->set_strategy(relnum,
+                                                   Relocatable_relocs::RELOC_SPECIAL);
+                       }
+                     return true;
+                   }
+               }
+           }
+         break;
        }
     }
 
@@ -8357,8 +8408,21 @@ Target_powerpc<size, big_endian>::relocate_relocs(
        }
       else if (strategy == Relocatable_relocs::RELOC_SPECIAL)
        {
-         if (addend >= 32768)
-           addend += got2_addend;
+         if (size == 32)
+           {
+             if (addend >= 32768)
+               addend += got2_addend;
+           }
+         else if (r_type == elfcpp::R_POWERPC_REL16_HA)
+           {
+             r_type = elfcpp::R_POWERPC_ADDR16_HA;
+             addend -= 2 * big_endian;
+           }
+         else if (r_type == elfcpp::R_POWERPC_REL16_LO)
+           {
+             r_type = elfcpp::R_POWERPC_ADDR16_LO;
+             addend -= 2 * big_endian + 4;
+           }
        }
       else
        gold_unreachable();