[GOLD] Add PowerPC64 -fsplit-stack support
authorAlan Modra <amodra@gmail.com>
Wed, 13 May 2015 04:42:38 +0000 (14:12 +0930)
committerAlan Modra <amodra@gmail.com>
Sat, 16 May 2015 13:19:57 +0000 (22:49 +0930)
PowerPC64 ELFv1 requires a tweak to find_functions in order to return
code addresses, rather than OPD entry addresses.

* reloc.cc (Sized_relobj_file::find_functions): Use function_location.
* powerpc.cc (Target_powerpc::do_calls_non_split): New function.
(addi_12_1, addis_2_12, addis_12_1, cmpld_7_12_0): New constants.
(lis_0): Rename from lis_0_0.

gold/ChangeLog
gold/powerpc.cc
gold/reloc.cc

index 758e8aab77fe0ec73e61f2728983935c29275828..64ce33f5a8e3d23fa17fd2a435b876ad5ac049ed 100644 (file)
@@ -1,3 +1,10 @@
+2015-05-16  Alan Modra  <amodra@gmail.com>
+
+       * reloc.cc (Sized_relobj_file::find_functions): Use function_location.
+       * powerpc.cc (Target_powerpc::do_calls_non_split): New function.
+       (addi_12_1, addis_2_12, addis_12_1, cmpld_7_12_0): New constants.
+       (lis_0): Rename from lis_0_0.
+
 2015-04-29  Cary Coutant  <cary@google.com>
            Rafael Ávila de Espíndola <rafael.espindola@gmail.com>
 
index 3e9fe8176d9693ee92ad6e22c41593dbb4166f36..540e1977c62df0ae9ddb3aa8ff87459ca6cbb07e 100644 (file)
@@ -624,6 +624,13 @@ class Target_powerpc : public Sized_target<size, big_endian>
   do_can_check_for_function_pointers() const
   { return true; }
 
+  // Adjust -fsplit-stack code which calls non-split-stack code.
+  void
+  do_calls_non_split(Relobj* object, unsigned int shndx,
+                    section_offset_type fnoffset, section_size_type fnsize,
+                    unsigned char* view, section_size_type view_size,
+                    std::string* from, std::string* to) const;
+
   // Relocate a section.
   void
   relocate_section(const Relocate_info<size, big_endian>*,
@@ -3175,12 +3182,15 @@ static const uint32_t addi_0_12         = 0x380c0000;
 static const uint32_t addi_2_2         = 0x38420000;
 static const uint32_t addi_3_3         = 0x38630000;
 static const uint32_t addi_11_11       = 0x396b0000;
+static const uint32_t addi_12_1                = 0x39810000;
 static const uint32_t addi_12_12       = 0x398c0000;
 static const uint32_t addis_0_2                = 0x3c020000;
 static const uint32_t addis_0_13       = 0x3c0d0000;
+static const uint32_t addis_2_12       = 0x3c4c0000;
 static const uint32_t addis_11_2       = 0x3d620000;
 static const uint32_t addis_11_11      = 0x3d6b0000;
 static const uint32_t addis_11_30      = 0x3d7e0000;
+static const uint32_t addis_12_1       = 0x3d810000;
 static const uint32_t addis_12_2       = 0x3d820000;
 static const uint32_t addis_12_12      = 0x3d8c0000;
 static const uint32_t b                        = 0x48000000;
@@ -3188,6 +3198,7 @@ static const uint32_t bcl_20_31           = 0x429f0005;
 static const uint32_t bctr             = 0x4e800420;
 static const uint32_t blr              = 0x4e800020;
 static const uint32_t bnectr_p4                = 0x4ce20420;
+static const uint32_t cmpld_7_12_0     = 0x7fac0040;
 static const uint32_t cmpldi_2_0       = 0x28220000;
 static const uint32_t cror_15_15_15    = 0x4def7b82;
 static const uint32_t cror_31_31_31    = 0x4ffffb82;
@@ -3204,7 +3215,7 @@ 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;
-static const uint32_t lis_0_0          = 0x3c000000;
+static const uint32_t lis_0            = 0x3c000000;
 static const uint32_t lis_11           = 0x3d600000;
 static const uint32_t lis_12           = 0x3d800000;
 static const uint32_t lvx_0_12_0       = 0x7c0c00ce;
@@ -4619,7 +4630,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
                    }
                  else
                    {
-                     write_insn<big_endian>(p, lis_0_0 + hi(indx)),    p += 4;
+                     write_insn<big_endian>(p, lis_0 + hi(indx)),      p += 4;
                      write_insn<big_endian>(p, ori_0_0_0 + l(indx)),   p += 4;
                    }
                }
@@ -6479,6 +6490,113 @@ Target_powerpc<size, big_endian>::do_function_location(
     }
 }
 
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fsplit-stack.  The function calls non-split-stack
+// code.  Change the function to ensure it has enough stack space to
+// call some random function.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_calls_non_split(
+    Relobj* object,
+    unsigned int shndx,
+    section_offset_type fnoffset,
+    section_size_type fnsize,
+    unsigned char* view,
+    section_size_type view_size,
+    std::string* from,
+    std::string* to) const
+{
+  // 32-bit not supported.
+  if (size == 32)
+    {
+      // warn
+      Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
+                                view, view_size, from, to);
+      return;
+    }
+
+  // The function always starts with
+  //   ld %r0,-0x7000-64(%r13)  # tcbhead_t.__private_ss
+  //   addis %r12,%r1,-allocate@ha
+  //   addi %r12,%r12,-allocate@l
+  //   cmpld %r12,%r0
+  // but note that the addis or addi may be replaced with a nop
+
+  unsigned char *entry = view + fnoffset;
+  uint32_t insn = elfcpp::Swap<32, big_endian>::readval(entry);
+
+  if ((insn & 0xffff0000) == addis_2_12)
+    {
+      /* Skip ELFv2 global entry code.  */
+      entry += 8;
+      insn = elfcpp::Swap<32, big_endian>::readval(entry);
+    }
+
+  unsigned char *pinsn = entry;
+  bool ok = false;
+  const uint32_t ld_private_ss = 0xe80d8fc0;
+  if (insn == ld_private_ss)
+    {
+      int32_t allocate = 0;
+      while (1)
+       {
+         pinsn += 4;
+         insn = elfcpp::Swap<32, big_endian>::readval(pinsn);
+         if ((insn & 0xffff0000) == addis_12_1)
+           allocate += (insn & 0xffff) << 16;
+         else if ((insn & 0xffff0000) == addi_12_1
+                  || (insn & 0xffff0000) == addi_12_12)
+           allocate += ((insn & 0xffff) ^ 0x8000) - 0x8000;
+         else if (insn != nop)
+           break;
+       }
+      if (insn == cmpld_7_12_0 && pinsn == entry + 12)
+       {
+         int extra = parameters->options().split_stack_adjust_size();
+         allocate -= extra;
+         if (allocate >= 0 || extra < 0)
+           {
+             object->error(_("split-stack stack size overflow at "
+                             "section %u offset %0zx"),
+                           shndx, static_cast<size_t>(fnoffset));
+             return;
+           }
+         pinsn = entry + 4;
+         insn = addis_12_1 | (((allocate + 0x8000) >> 16) & 0xffff);
+         if (insn != addis_12_1)
+           {
+             elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+             pinsn += 4;
+             insn = addi_12_12 | (allocate & 0xffff);
+             if (insn != addi_12_12)
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+                 pinsn += 4;
+               }
+           }
+         else
+           {
+             insn = addi_12_1 | (allocate & 0xffff);
+             elfcpp::Swap<32, big_endian>::writeval(pinsn, insn);
+             pinsn += 4;
+           }
+         if (pinsn != entry + 12)
+           elfcpp::Swap<32, big_endian>::writeval(pinsn, nop);
+
+         ok = true;
+       }
+    }
+
+  if (!ok)
+    {
+      if (!object->has_no_split_stack())
+       object->error(_("failed to match split-stack sequence at "
+                       "section %u offset %0zx"),
+                     shndx, static_cast<size_t>(fnoffset));
+    }
+}
+
 // Scan relocations for a section.
 
 template<int size, bool big_endian>
index 910c4ee9ed36b406f5f615f1e09779f10e02e8fa..3c9f7a9e6dae1731c794d42433ebeac439423992 100644 (file)
@@ -1415,13 +1415,21 @@ Sized_relobj_file<size, big_endian>::find_functions(
        continue;
 
       bool is_ordinary;
-      unsigned int sym_shndx = this->adjust_sym_shndx(i, isym.get_st_shndx(),
-                                                     &is_ordinary);
-      if (!is_ordinary || sym_shndx != shndx)
+      Symbol_location loc;
+      loc.shndx = this->adjust_sym_shndx(i, isym.get_st_shndx(),
+                                        &is_ordinary);
+      if (!is_ordinary)
+       continue;
+
+      loc.object = this;
+      loc.offset = isym.get_st_value();
+      parameters->target().function_location(&loc);
+
+      if (loc.shndx != shndx)
        continue;
 
       section_offset_type value =
-       convert_to_section_size_type(isym.get_st_value());
+       convert_to_section_size_type(loc.offset);
       section_size_type fnsize =
        convert_to_section_size_type(isym.get_st_size());