public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
@ 2015-12-09 15:15 Marcin Kościelnicki
  2015-12-10  6:25 ` Cary Coutant
  0 siblings, 1 reply; 11+ messages in thread
From: Marcin Kościelnicki @ 2015-12-09 15:15 UTC (permalink / raw)
  To: binutils; +Cc: Marcin Kościelnicki

This adds split-stack support to gold for s390.  The corresponding gcc
changes are at https://github.com/koriakin/gcc/tree/s390-ss .  Roughly,
the split-stack prologue for s390 works as follows:

- if the stack frame is small enough that its size fits in an add
  instruction (alfi, ahi, algfi, aghi - whichever is supported),
  the stack guard (from TLS) is compared to stack pointer - frame
  size, and if there's enough space, a direct jump is made to the
  function body (skipping the call).  If the stack frame is large,
  __morestack will do the check anyway.
- otherwise, __morestack is called.  Since there's only one easily
  available register (%r1), and the parameters are link-time constants,
  the parameters are gathered in a static memory block, and its address
  is passed in %r1.  One of the parameters is the frame size.  For ESA
  mode, the parameter block resides directly in the function code (like
  a literal pool), while for z/Arch, it resides in .rodata section (for
  cache-related reasons).
- as usual, if a split-stack function is found to call a non-split-stack
  function at link time, it needs to be "fixed" by the linker.  In case
  of s390, it involves bumping the frame size in the parameter block,
  bumping the frame size in the add instruction (if applicable), or
  nopping out the whole comparison if it can no longer fit in the add
  instruction.

The problem here is that I have to get to the proper place in .rodata
(and mutate it), starting from the text section by parsing the
relocation of the larl instruction aiming at it.  The parameters
currently passed into do_calls_non_split unfortunately don't seem to
allow this.

I have managed to get it working, but only by making a lot of private
or protected things public, and changing some types.  In summary:

- the private Views class used in reloc.cc is made public and routed
  to do_calls_non_split.
- Relobj's output_sections method is likewise made public.
- text section's relocation pointer and count are also routed to
  do_calls_non_split.
- since calls_non_split now takes a templated-type parameter (the reloc
  pointer), it's moved to Sized_target class, and a few ugly dynamic
  casts are inserted.

Overall, the changes are pretty horrible.  Is there some better way
to walk through a relocation in the text section to a place in another
section?  The current patch seems way too intrusive to me.

Thanks for any suggestions.

---
 configure                         |   2 +-
 gold/Makefile.in                  |   2 +-
 gold/i386.cc                      |  10 +-
 gold/object.h                     |  54 ++---
 gold/powerpc.cc                   |  15 +-
 gold/reloc.cc                     |  18 +-
 gold/s390.cc                      | 440 ++++++++++++++++++++++++++++++++++++++
 gold/target.cc                    |  43 ++--
 gold/target.h                     |  62 ++++--
 gold/testsuite/Makefile.am        | 204 ++++++++++++++++++
 gold/testsuite/Makefile.in        | 215 ++++++++++++++++++-
 gold/testsuite/split_s390.sh      | 149 +++++++++++++
 gold/testsuite/split_s390_1_a1.s  |  36 ++++
 gold/testsuite/split_s390_1_a2.s  |  37 ++++
 gold/testsuite/split_s390_1_e1.s  |  45 ++++
 gold/testsuite/split_s390_1_e2.s  |  48 +++++
 gold/testsuite/split_s390_1_e3.s  |  49 +++++
 gold/testsuite/split_s390_1_e4.s  |  51 +++++
 gold/testsuite/split_s390_1_n.s   |  18 ++
 gold/testsuite/split_s390_1_z1.s  |  37 ++++
 gold/testsuite/split_s390_1_z2.s  |  40 ++++
 gold/testsuite/split_s390_1_z3.s  |  41 ++++
 gold/testsuite/split_s390_1_z4.s  |  41 ++++
 gold/testsuite/split_s390_2_ns.s  |  12 ++
 gold/testsuite/split_s390_2_s.s   |  13 ++
 gold/testsuite/split_s390x_1_a1.s |  27 +++
 gold/testsuite/split_s390x_1_a2.s |  28 +++
 gold/testsuite/split_s390x_1_n.s  |  17 ++
 gold/testsuite/split_s390x_1_z1.s |  37 ++++
 gold/testsuite/split_s390x_1_z2.s |  41 ++++
 gold/testsuite/split_s390x_1_z3.s |  43 ++++
 gold/testsuite/split_s390x_1_z4.s |  43 ++++
 gold/testsuite/split_s390x_2_ns.s |  12 ++
 gold/testsuite/split_s390x_2_s.s  |  13 ++
 gold/x86_64.cc                    |  10 +-
 35 files changed, 1865 insertions(+), 88 deletions(-)
 create mode 100755 gold/testsuite/split_s390.sh
 create mode 100644 gold/testsuite/split_s390_1_a1.s
 create mode 100644 gold/testsuite/split_s390_1_a2.s
 create mode 100644 gold/testsuite/split_s390_1_e1.s
 create mode 100644 gold/testsuite/split_s390_1_e2.s
 create mode 100644 gold/testsuite/split_s390_1_e3.s
 create mode 100644 gold/testsuite/split_s390_1_e4.s
 create mode 100644 gold/testsuite/split_s390_1_n.s
 create mode 100644 gold/testsuite/split_s390_1_z1.s
 create mode 100644 gold/testsuite/split_s390_1_z2.s
 create mode 100644 gold/testsuite/split_s390_1_z3.s
 create mode 100644 gold/testsuite/split_s390_1_z4.s
 create mode 100644 gold/testsuite/split_s390_2_ns.s
 create mode 100644 gold/testsuite/split_s390_2_s.s
 create mode 100644 gold/testsuite/split_s390x_1_a1.s
 create mode 100644 gold/testsuite/split_s390x_1_a2.s
 create mode 100644 gold/testsuite/split_s390x_1_n.s
 create mode 100644 gold/testsuite/split_s390x_1_z1.s
 create mode 100644 gold/testsuite/split_s390x_1_z2.s
 create mode 100644 gold/testsuite/split_s390x_1_z3.s
 create mode 100644 gold/testsuite/split_s390x_1_z4.s
 create mode 100644 gold/testsuite/split_s390x_2_ns.s
 create mode 100644 gold/testsuite/split_s390x_2_s.s

diff --git a/configure b/configure
index 3bb1c03..91fd098 100755
--- a/configure
+++ b/configure
@@ -2972,7 +2972,7 @@ case "${ENABLE_GOLD}" in
       # Check for target supported by gold.
       case "${target}" in
         i?86-*-* | x86_64-*-* | sparc*-*-* | powerpc*-*-* | arm*-*-* \
-        | aarch64*-*-* | tilegx*-*-*)
+        | aarch64*-*-* | tilegx*-*-* | s390*-*-*)
 	  configdirs="$configdirs gold"
 	  if test x${ENABLE_GOLD} = xdefault; then
 	    default_ld=gold
diff --git a/gold/Makefile.in b/gold/Makefile.in
index dbfde80..aa04f8b 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -71,7 +71,7 @@ DIST_COMMON = NEWS README ChangeLog $(srcdir)/Makefile.in \
 	$(srcdir)/Makefile.am $(top_srcdir)/configure \
 	$(am__configure_deps) $(srcdir)/config.in \
 	$(srcdir)/../mkinstalldirs $(top_srcdir)/po/Make-in pread.c \
-	ffsll.c ftruncate.c mremap.c yyscript.h yyscript.c \
+	mremap.c ffsll.c ftruncate.c yyscript.h yyscript.c \
 	$(srcdir)/../depcomp $(srcdir)/../ylwrap
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \
diff --git a/gold/i386.cc b/gold/i386.cc
index 8cc7244..1d6adf3 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -487,12 +487,15 @@ class Target_i386 : public Sized_target<32, false>
   bool
   do_is_call_to_non_split(const Symbol* sym, unsigned int) const;
 
+  typedef typename Sized_relobj_file<32, false>::Views Views;
+
   // 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;
+		     std::string* from, std::string* to,
+		     const unsigned char *, size_t, Views *) const;
 
   // Return the size of the GOT section.
   section_size_type
@@ -3884,7 +3887,10 @@ Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx,
 				       unsigned char* view,
 				       section_size_type view_size,
 				       std::string* from,
-				       std::string* to) const
+				       std::string* to,
+				       const unsigned char *,
+				       size_t,
+				       Views *) const
 {
   // The function starts with a comparison of the stack pointer and a
   // field in the TCB.  This is followed by a jump.
diff --git a/gold/object.h b/gold/object.h
index f408408..3268fae 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -1365,12 +1365,21 @@ class Relobj : public Object
   is_big_endian() const
   { return this->do_is_big_endian(); }
 
- protected:
   // The output section to be used for each input section, indexed by
   // the input section number.  The output section is NULL if the
   // input section is to be discarded.
   typedef std::vector<Output_section*> Output_sections;
 
+  // Return the vector mapping input sections to output sections.
+  Output_sections&
+  output_sections()
+  { return this->output_sections_; }
+
+  const Output_sections&
+  output_sections() const
+  { return this->output_sections_; }
+
+ protected:
   // Read the relocs--implemented by child class.
   virtual void
   do_read_relocs(Read_relocs_data*) = 0;
@@ -1466,15 +1475,6 @@ class Relobj : public Object
     return this->output_sections_[shndx];
   }
 
-  // Return the vector mapping input sections to output sections.
-  Output_sections&
-  output_sections()
-  { return this->output_sections_; }
-
-  const Output_sections&
-  output_sections() const
-  { return this->output_sections_; }
-
   // Set the size of the relocatable relocs array.
   void
   size_relocatable_relocs()
@@ -2316,6 +2316,20 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
   bool is_deferred_layout() const
   { return this->is_deferred_layout_; }
 
+  // Views and sizes when relocating.
+  struct View_size
+  {
+    unsigned char* view;
+    typename elfcpp::Elf_types<size>::Elf_Addr address;
+    off_t offset;
+    section_size_type view_size;
+    bool is_input_output_view;
+    bool is_postprocessing_view;
+    bool is_ctors_reverse_view;
+  };
+
+  typedef std::vector<View_size> Views;
+
  protected:
   typedef typename Sized_relobj<size, big_endian>::Output_sections
       Output_sections;
@@ -2527,20 +2541,6 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
   local_values()
   { return &this->local_values_; }
 
-  // Views and sizes when relocating.
-  struct View_size
-  {
-    unsigned char* view;
-    typename elfcpp::Elf_types<size>::Elf_Addr address;
-    off_t offset;
-    section_size_type view_size;
-    bool is_input_output_view;
-    bool is_postprocessing_view;
-    bool is_ctors_reverse_view;
-  };
-
-  typedef std::vector<View_size> Views;
-
   // Stash away info for a number of special sections.
   // Return true if any of the sections found require local symbols to be read.
   virtual bool
@@ -2696,7 +2696,8 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
 		     unsigned int sh_type, unsigned int shndx,
 		     const unsigned char* prelocs, size_t reloc_count,
 		     unsigned char* view, section_size_type view_size,
-		     Reloc_symbol_changes** reloc_map);
+		     Reloc_symbol_changes** reloc_map,
+		     Views *pviews);
 
   template<int sh_type>
   void
@@ -2704,7 +2705,8 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
 			     unsigned int shndx, const unsigned char* prelocs,
 			     size_t reloc_count, unsigned char* view,
 			     section_size_type view_size,
-			     Reloc_symbol_changes** reloc_map);
+			     Reloc_symbol_changes** reloc_map,
+			     Views *pviews);
 
   // Find all functions in a section.
   void
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 59d04b3..25a7288 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -627,12 +627,15 @@ class Target_powerpc : public Sized_target<size, big_endian>
   do_can_check_for_function_pointers() const
   { return true; }
 
+  typedef typename Sized_relobj_file<size, big_endian>::Views Views;
+
   // 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;
+		     std::string* from, std::string* to,
+		     const unsigned char *, size_t, Views *) const;
 
   // Relocate a section.
   void
@@ -6593,14 +6596,18 @@ Target_powerpc<size, big_endian>::do_calls_non_split(
     unsigned char* view,
     section_size_type view_size,
     std::string* from,
-    std::string* to) const
+    std::string* to,
+    const unsigned char *prelocs,
+    size_t reloc_count,
+    Views *pviews) const
 {
   // 32-bit not supported.
   if (size == 32)
     {
       // warn
-      Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
-				 view, view_size, from, to);
+      Sized_target<size, big_endian>::do_calls_non_split(object, shndx, fnoffset, fnsize,
+				 view, view_size, from, to,
+				 prelocs, reloc_count, pviews);
       return;
     }
 
diff --git a/gold/reloc.cc b/gold/reloc.cc
index f18f432..995b447 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -1007,7 +1007,7 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections(
 	  if ((data_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0)
 	    this->split_stack_adjust(symtab, pshdrs, sh_type, index,
 				     prelocs, reloc_count, view, view_size,
-				     &reloc_map);
+				     &reloc_map, pviews);
 	}
 
       Relocatable_relocs* rr = NULL;
@@ -1207,20 +1207,21 @@ Sized_relobj_file<size, big_endian>::split_stack_adjust(
     size_t reloc_count,
     unsigned char* view,
     section_size_type view_size,
-    Reloc_symbol_changes** reloc_map)
+    Reloc_symbol_changes** reloc_map,
+    Views *pviews)
 {
   if (sh_type == elfcpp::SHT_REL)
     this->split_stack_adjust_reltype<elfcpp::SHT_REL>(symtab, pshdrs, shndx,
 						      prelocs, reloc_count,
 						      view, view_size,
-						      reloc_map);
+						      reloc_map, pviews);
   else
     {
       gold_assert(sh_type == elfcpp::SHT_RELA);
       this->split_stack_adjust_reltype<elfcpp::SHT_RELA>(symtab, pshdrs, shndx,
 							 prelocs, reloc_count,
 							 view, view_size,
-							 reloc_map);
+							 reloc_map, pviews);
     }
 }
 
@@ -1238,7 +1239,8 @@ Sized_relobj_file<size, big_endian>::split_stack_adjust_reltype(
     size_t reloc_count,
     unsigned char* view,
     section_size_type view_size,
-    Reloc_symbol_changes** reloc_map)
+    Reloc_symbol_changes** reloc_map,
+    Views *pviews)
 {
   typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
   const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
@@ -1330,8 +1332,10 @@ Sized_relobj_file<size, big_endian>::split_stack_adjust_reltype(
     {
       std::string from;
       std::string to;
-      parameters->target().calls_non_split(this, shndx, p->first, p->second,
-					   view, view_size, &from, &to);
+      dynamic_cast<const Sized_target<size, big_endian> &>(parameters->target())
+			  .calls_non_split(this, shndx, p->first, p->second,
+					   view, view_size, &from, &to,
+					   prelocs, reloc_count, pviews);
       if (!from.empty())
 	{
 	  gold_assert(!to.empty());
diff --git a/gold/s390.cc b/gold/s390.cc
index 45c0ba7..caba543 100644
--- a/gold/s390.cc
+++ b/gold/s390.cc
@@ -402,6 +402,17 @@ class Target_s390 : public Sized_target<size, true>
   do_can_check_for_function_pointers() const
   { return true; }
 
+  typedef typename Sized_relobj_file<size, true>::Views Views;
+
+  // 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 unsigned char *prelocs,
+		     size_t reloc_count, Views *pviews) const;
+
   // Return the size of the GOT section.
   section_size_type
   got_size() const
@@ -724,6 +735,22 @@ class Target_s390 : public Sized_target<size, true>
   bool tls_base_symbol_defined_;
   // For use in do_tls_offset_for_*
   Layout *layout_;
+
+  // Code sequences for -fsplit-stack matching.
+  static const unsigned char ss_code_nopmark[];
+  static const unsigned char ss_code_ear[];
+  static const unsigned char ss_code_c[];
+  static const unsigned char ss_code_l[];
+  static const unsigned char ss_code_ahi[];
+  static const unsigned char ss_code_alfi[];
+  static const unsigned char ss_code_cr[];
+  static const unsigned char ss_code_larl[];
+  static const unsigned char ss_code_jg[];
+  static const unsigned char ss_code_jgl[];
+  static const unsigned char ss_code_jl[];
+  static const unsigned char ss_code_jhe[];
+  static const unsigned char ss_code_basr[];
+  static const unsigned char ss_code_basr_2[];
 };
 
 template<>
@@ -4243,6 +4270,419 @@ Target_s390<size>::do_code_fill(section_size_type length) const
   return std::string(length, static_cast<char>(0x07));
 }
 
+// Code sequences to match below.
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_nopmark[] = {
+  0x07, 0x0f,				// nopr %r15
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_ear[] = {
+  0xb2, 0x4f, 0x00, 0x10,		// ear %r1, %a0
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_ear[] = {
+  0xb2, 0x4f, 0x00, 0x10,		// ear %r1, %a0
+  0xeb, 0x11, 0x00, 0x20, 0x00, 0x0d,	// sllg %r1,%r1,32
+  0xb2, 0x4f, 0x00, 0x11,		// ear %r1, %a1
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_c[] = {
+  0x59, 0xf0, 0x10, 0x20,		// c %r15, 0x20(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_c[] = {
+  0xe3, 0xf0, 0x10, 0x38, 0x00, 0x20,	// cg %r15, 0x38(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_l[] = {
+  0x58, 0x10, 0x10, 0x20,		// l %r1, 0x20(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_l[] = {
+  0xe3, 0x10, 0x10, 0x38, 0x00, 0x04,	// lg %r1, 0x38(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_ahi[] = {
+  0xa7, 0x1a,				// ahi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_ahi[] = {
+  0xa7, 0x1b,				// aghi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_alfi[] = {
+  0xc2, 0x1b,				// alfi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_alfi[] = {
+  0xc2, 0x1a,				// algfi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_cr[] = {
+  0x19, 0xf1,				// cr %r15, %r1
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_cr[] = {
+  0xb9, 0x20, 0x00, 0xf1,		// cgr %r15, %r1
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_larl[] = {
+  0xc0, 0x10		,		// larl ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jg[] = {
+  0xc0, 0xf4		,		// jg ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jgl[] = {
+  0xc0, 0x44,				// jgl ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jl[] = {
+  0xa7, 0x44,				// jl ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jhe[] = {
+  0xa7, 0xa4,				// jhe ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_basr[] = {
+  0x0d, 0x10,				// basr %r1, 0
+  0x5a, 0x10, 0x10, 0x12,		// a %r1, 0x12(%r1)
+  0x0d, 0x11,				// basr %r1, %r1
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_basr_2[] = {
+  0x0d, 0x10,				// basr %r1, 0
+  0x5a, 0x10, 0x10, 0x14,		// a %r1, 0x14(%r1)
+  0x0d, 0x11,				// basr %r1, %r1
+};
+
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fsplit-stack.  The function calls non-split-stack
+// code.  We have to change the function so that it always ensures
+// that it has enough stack space to run some random function.
+
+template<int size>
+void
+Target_s390<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
+				      section_offset_type fnoffset,
+				      section_size_type,
+				      unsigned char* view,
+				      section_size_type view_size,
+				      std::string*,
+				      std::string*,
+				      const unsigned char *prelocs,
+				      size_t reloc_count,
+				      Views *pviews) const
+{
+  // true if there's a conditional call to __morestack in the function,
+  // false if there's an unconditional one.
+  bool conditional = false;
+  // Offset of the byte after the compare insn, if conditional.
+  section_offset_type cmpend = 0;
+  // Type and immediate offset of the add instruction that adds frame size
+  // to guard.
+  enum {
+    SS_ADD_NONE,
+    SS_ADD_AHI,
+    SS_ADD_ALFI,
+  } fsadd_type = SS_ADD_NONE;
+  section_offset_type fsadd_offset = 0;
+  uint32_t fsadd_frame_size = 0;
+  // Type and offset of the conditional jump.
+  enum {
+    SS_JUMP_NONE,
+    SS_JUMP_JL,
+    SS_JUMP_JHE,
+  } jump_type = SS_JUMP_NONE;
+  section_offset_type jump_offset = 0;
+  // Section view and offset of param block.
+  section_offset_type param_offset = 0;
+  unsigned char *param_view = 0;
+  section_size_type param_view_size = 0;
+  // Current position in function.
+  section_offset_type curoffset = fnoffset;
+  // Frame size.
+  typename elfcpp::Elf_types<size>::Elf_Addr frame_size;
+  // Relocation parsing.
+  typedef typename Reloc_types<elfcpp::SHT_RELA, size, true>::Reloc Reltype;
+  const int reloc_size = Reloc_types<elfcpp::SHT_RELA, size, true>::reloc_size;
+
+  // If the function starts with a nopr %r15, it's a split-stack-compiled 
+  // function that doesn't have a stack frame.  Ignore it.
+
+  if (this->match_view(view, view_size, curoffset, ss_code_nopmark,
+		       sizeof ss_code_nopmark))
+    return;
+
+  // First, figure out if there's a conditional call by looking for the
+  // extract-tp, add, cmp sequence.
+
+  if (this->match_view(view, view_size, curoffset, ss_code_ear,
+		       sizeof ss_code_ear))
+    {
+      // Found extract-tp, now look for an add and compare.
+      curoffset += sizeof ss_code_ear;
+      conditional = true;
+      if (this->match_view(view, view_size, curoffset, ss_code_c,
+			   sizeof ss_code_c))
+	{
+	  // Found a direct compare of stack pointer with the guard,
+	  // we're done here.
+	  curoffset += sizeof ss_code_c;
+	}
+      else if (this->match_view(view, view_size, curoffset, ss_code_l,
+			   sizeof ss_code_l))
+	{
+	  // Found a load of guard to r1, look for an add and compare.
+	  curoffset += sizeof ss_code_l;
+          if (this->match_view(view, view_size, curoffset, ss_code_ahi,
+			       sizeof ss_code_ahi))
+	    {
+	      curoffset += sizeof ss_code_ahi;
+	      // Found an ahi, extract its immediate
+	      fsadd_offset = curoffset;
+	      fsadd_type = SS_ADD_AHI;
+	      if (static_cast<section_size_type>(curoffset + 2) > view_size)
+	        goto bad;
+	      fsadd_frame_size
+	        = elfcpp::Swap<16, true>::readval(view + curoffset);
+	      curoffset += 2;
+	    }
+	  else if (this->match_view(view, view_size, curoffset,
+		    ss_code_alfi, sizeof ss_code_alfi))
+	    {
+	      curoffset += sizeof ss_code_alfi;
+	      // Found an alfi, extract its immediate
+	      fsadd_offset = curoffset;
+	      fsadd_type = SS_ADD_ALFI;
+	      if (static_cast<section_size_type>(curoffset + 4) > view_size)
+	        goto bad;
+	      fsadd_frame_size
+	        = elfcpp::Swap_unaligned<32, true>::readval(view + curoffset);
+	      curoffset += 4;
+	    }
+	  else
+            {
+	      goto bad;
+            }
+	  // Now, there has to be a compare.
+          if (this->match_view(view, view_size, curoffset, ss_code_cr,
+			       sizeof ss_code_cr))
+	    curoffset += sizeof ss_code_cr;
+	  else
+	    goto bad;
+	}
+      else
+        {
+	  goto bad;
+        }
+      cmpend = curoffset;
+    }
+
+  // Second, look for the call.
+
+  if (this->match_view(view, view_size, curoffset, ss_code_larl,
+		       sizeof ss_code_larl))
+    {
+      // Found a zarch-style call: larl + jg
+      curoffset += sizeof ss_code_larl;
+
+      // Find out larl's operand.  It should be a local symbol in .rodata
+      // section.
+      const unsigned char *pr = prelocs;
+      for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size)
+        {
+          Reltype reloc(pr);
+	  if (static_cast<section_offset_type>(reloc.get_r_offset()) == curoffset)
+	    {
+              typename elfcpp::Elf_types<size>::Elf_WXword r_info
+	        = reloc.get_r_info();
+              unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+              unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+	      if (r_type != elfcpp::R_390_PC32DBL)
+	        goto bad;
+	      if (r_sym >= object->local_symbol_count())
+		goto bad;
+              const Symbol_value<size>* sym = dynamic_cast<Sized_relobj_file<size, true> *>(object)->local_symbol(r_sym);
+	      bool param_shndx_ordinary;
+	      const unsigned int param_shndx = sym->input_shndx(&param_shndx_ordinary);
+	      if (!param_shndx_ordinary)
+		goto bad;
+	      param_offset = sym->input_value() + reloc.get_r_addend() - 2 - object->output_sections()[param_shndx]->address();
+	      param_view = (*pviews)[param_shndx].view;
+	      param_view_size = (*pviews)[param_shndx].view_size;
+	      break;
+	    }
+	}
+
+      if (!param_view)
+	goto bad;
+
+      curoffset += 4;
+
+      // Now, there has to be a jump to __morestack.
+      jump_offset = curoffset;
+
+      if (this->match_view(view, view_size, curoffset,
+			   conditional ? ss_code_jgl : ss_code_jg,
+			   sizeof ss_code_jg))
+	curoffset += sizeof ss_code_jg;
+      else
+	goto bad;
+
+      if (conditional)
+        jump_type = SS_JUMP_JL;
+
+      curoffset += 4;
+    }
+  else if (size == 32)
+    {
+      // There'll be an ESA-style call, possibly with a cond jump first.
+      if (conditional)
+	{
+	  if (this->match_view(view, view_size, curoffset, ss_code_jl,
+			       sizeof ss_code_jl))
+	    {
+	      // Jump if morestack call needed - we need to follow it.
+	      int16_t off;
+	      jump_type = SS_JUMP_JL;
+	      jump_offset = curoffset;
+	      curoffset += sizeof ss_code_jl;
+	      if (static_cast<section_size_type>(curoffset + 2) > view_size)
+	        goto bad;
+	      off = elfcpp::Swap<16, true>::readval(view + curoffset);
+	      curoffset += off * 2 - sizeof ss_code_jl;
+	    }
+	  else if (this->match_view(view, view_size, curoffset, ss_code_jhe,
+				    sizeof ss_code_jhe))
+	    {
+	      jump_type = SS_JUMP_JHE;
+	      // Jump if morestack not needed - skip over it.  Bump cmpend,
+	      // so that jump is nopped out along with the rest if we need
+	      // to make the call unconditional.
+	      curoffset += sizeof ss_code_jhe;
+	      curoffset += 2;
+	      cmpend = curoffset;
+	    }
+	  else
+	    {
+	      goto bad;
+	    }
+	}
+
+      // Match the basr sequence. There are two variants, depending on
+      // whether the start is 4-aligned or not.
+      if (this->match_view(view, view_size, curoffset, ss_code_basr,
+			   sizeof ss_code_basr))
+	curoffset += sizeof ss_code_basr;
+      else if (this->match_view(view, view_size, curoffset, ss_code_basr_2,
+			   sizeof ss_code_basr_2))
+	curoffset += sizeof ss_code_basr_2 + 2;
+      else
+        goto bad;
+
+      // We found the param block.
+      param_offset = curoffset;
+      param_view = view;
+      param_view_size = view_size;
+    }
+  else
+    {
+      goto bad;
+    }
+
+  // Read the frame size.
+  if (static_cast<section_size_type>(param_offset + size / 8) > param_view_size)
+    goto bad;
+  frame_size = elfcpp::Swap<size, true>::readval(param_view + param_offset);
+
+  // Sanity check.
+  if (fsadd_type != SS_ADD_NONE && fsadd_frame_size != frame_size)
+    goto bad;
+
+  // Bump the frame size.
+  frame_size += parameters->options().split_stack_adjust_size();
+
+  // Store it to the param block.
+  elfcpp::Swap<size, true>::writeval(param_view + param_offset, frame_size);
+
+  if (!conditional)
+    {
+      // If the call was already unconditional, we're done.
+    }
+  else if (frame_size <= 0xffffffff && fsadd_type == SS_ADD_ALFI)
+    {
+      // Using alfi to add the frame size, and it still fits.  Adjust it.
+      elfcpp::Swap_unaligned<32, true>::writeval(view + fsadd_offset,
+						 frame_size);
+    }
+  else
+    {
+      // We were either relying on the backoff area, or used ahi to load
+      // frame size.  This won't fly, as our new frame size is too large.
+      // Convert the sequence to unconditional by nopping out the comparison,
+      // and rewiring the jump.
+      this->set_view_to_nop(view, view_size, fnoffset, cmpend - fnoffset);
+
+      // If jump type was jhe, we already nopped it out above.
+      if (jump_type == SS_JUMP_JL)
+        {
+	  // If jump is jl/jgl, we'll mutate it to j/jg.
+	  view[jump_offset+1] = 0xf4;
+	}
+    }
+
+  return;
+
+bad:
+  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));
+}
+
 // Relocate section data.
 
 template<int size>
diff --git a/gold/target.cc b/gold/target.cc
index f558c5e..8e74cb5 100644
--- a/gold/target.cc
+++ b/gold/target.cc
@@ -157,23 +157,6 @@ Target::do_is_call_to_non_split(const Symbol* sym, unsigned int) const
   return sym->type() == elfcpp::STT_FUNC;
 }
 
-// Default conversion for -fsplit-stack is to give an error.
-
-void
-Target::do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
-			   section_size_type, unsigned char*, section_size_type,
-			   std::string*, std::string*) const
-{
-  static bool warned;
-  if (!warned)
-    {
-      gold_error(_("linker does not include stack split support "
-		   "required by %s"),
-		 object->name().c_str());
-      warned = true;
-    }
-}
-
 //  Return whether BYTES/LEN matches VIEW/VIEW_SIZE at OFFSET.
 
 bool
@@ -237,6 +220,32 @@ Sized_target<size, big_endian>::do_adjust_elf_header(unsigned char* view,
     }
 }
 
+// Default conversion for -fsplit-stack is to give an error.
+
+template<int size, bool big_endian>
+void
+Sized_target<size, big_endian>::do_calls_non_split(Relobj* object,
+						   unsigned int,
+						   section_offset_type,
+						   section_size_type,
+						   unsigned char*,
+						   section_size_type,
+						   std::string*,
+						   std::string*,
+						   const unsigned char *,
+						   size_t,
+						   Views *) const
+{
+  static bool warned;
+  if (!warned)
+    {
+      gold_error(_("linker does not include stack split support "
+		   "required by %s"),
+		 object->name().c_str());
+      warned = true;
+    }
+}
+
 #ifdef HAVE_TARGET_32_LITTLE
 template
 class Sized_target<32, false>;
diff --git a/gold/target.h b/gold/target.h
index 6e08f38..04f51b8 100644
--- a/gold/target.h
+++ b/gold/target.h
@@ -38,6 +38,9 @@
 #include "parameters.h"
 #include "stringpool.h"
 #include "debug.h"
+#include "fileread.h"
+#include "reloc.h"
+#include "object.h"
 
 namespace gold
 {
@@ -329,22 +332,6 @@ class Target
   is_call_to_non_split(const Symbol* sym, unsigned int r_type) const
   { return this->do_is_call_to_non_split(sym, r_type); }
 
-  // A function starts at OFFSET in section SHNDX in OBJECT.  That
-  // function was compiled with -fsplit-stack, but it refers to a
-  // function which was compiled without -fsplit-stack.  VIEW is a
-  // modifiable view of the section; VIEW_SIZE is the size of the
-  // view.  The target has to adjust the function so that it allocates
-  // enough stack.
-  void
-  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
-  {
-    this->do_calls_non_split(object, shndx, fnoffset, fnsize, view, view_size,
-			     from, to);
-  }
-
   // Make an ELF object.
   template<int size, bool big_endian>
   Object*
@@ -661,12 +648,6 @@ class Target
   virtual bool
   do_is_call_to_non_split(const Symbol* sym, unsigned int) const;
 
-  // Virtual function which may be overridden by the child class.
-  virtual void
-  do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
-		     section_size_type, unsigned char*, section_size_type,
-		     std::string*, std::string*) const;
-
   // make_elf_object hooks.  There are four versions of these for
   // different address sizes and endianness.
 
@@ -727,6 +708,16 @@ class Target
   match_view(const unsigned char* view, section_size_type view_size,
 	     section_offset_type offset, const char* bytes, size_t len) const;
 
+  // A function for targets to call.  Return whether BYTES/LEN matches
+  // VIEW/VIEW_SIZE at OFFSET.
+  bool
+  match_view(const unsigned char* view, section_size_type view_size,
+	     section_offset_type offset, const unsigned char* bytes, size_t len) const
+    {
+      return this->match_view(view, view_size, offset,
+			      reinterpret_cast<const char *>(bytes), len);
+    }
+
   // Set the contents of a VIEW/VIEW_SIZE to nops starting at OFFSET
   // for LEN bytes.
   void
@@ -823,6 +814,8 @@ template<int size, bool big_endian>
 class Sized_target : public Target
 {
  public:
+  typedef typename Sized_relobj_file<size, big_endian>::Views Views;
+
   // Make a new symbol table entry for the target.  This should be
   // overridden by a target which needs additional information in the
   // symbol table.  This will only be called if has_make_symbol()
@@ -1081,6 +1074,24 @@ class Sized_target : public Target
 			      dst_obj, dst_shndx, dst_off);
   }
 
+  // A function starts at OFFSET in section SHNDX in OBJECT.  That
+  // function was compiled with -fsplit-stack, but it refers to a
+  // function which was compiled without -fsplit-stack.  VIEW is a
+  // modifiable view of the section; VIEW_SIZE is the size of the
+  // view.  The target has to adjust the function so that it allocates
+  // enough stack.
+  void
+  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 unsigned char *prelocs, size_t reloc_count,
+		  Views *pviews) const
+  {
+    this->do_calls_non_split(object, shndx, fnoffset, fnsize, view, view_size,
+			     from, to, prelocs, reloc_count, pviews);
+  }
+
  protected:
   Sized_target(const Target::Target_info* pti)
     : Target(pti)
@@ -1100,6 +1111,13 @@ class Sized_target : public Target
 		      typename elfcpp::Elf_types<size>::Elf_Addr) const
   { }
 
+  // Virtual function which may be overridden by the child class.
+  virtual void
+  do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
+		     section_size_type, unsigned char*, section_size_type,
+		     std::string*, std::string*, const unsigned char *,
+		     size_t, Views *) const;
+
   virtual void
   do_function_location(Symbol_location*) const
   { }
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index c8e5f9f..7cc7a2d 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -3362,6 +3362,210 @@ MOSTLYCLEANFILES += arm_farcall_thumb_arm arm_farcall_thumb_arm_5t
 
 endif DEFAULT_TARGET_ARM
 
+if DEFAULT_TARGET_S390
+
+check_SCRIPTS += split_s390.sh
+check_DATA += split_s390_e1.stdout split_s390_e2.stdout \
+	split_s390_e3.stdout split_s390_e4.stdout split_s390_z1.stdout \
+	split_s390_z2.stdout split_s390_z3.stdout split_s390_z4.stdout \
+	split_s390_n.stdout split_s390_a1.stdout split_s390_a2.stdout \
+	split_s390_e1_ns.stdout split_s390_e2_ns.stdout \
+	split_s390_e3_ns.stdout split_s390_e4_ns.stdout \
+	split_s390_z1_ns.stdout split_s390_z2_ns.stdout \
+	split_s390_z3_ns.stdout split_s390_z4_ns.stdout \
+	split_s390_n_ns.stdout split_s390_r.stdout \
+	split_s390x_z1.stdout split_s390x_z2.stdout split_s390x_z3.stdout \
+	split_s390x_z4.stdout split_s390x_n.stdout split_s390x_a1.stdout \
+	split_s390x_a2.stdout split_s390x_z1_ns.stdout \
+	split_s390x_z2_ns.stdout split_s390x_z3_ns.stdout \
+	split_s390x_z4_ns.stdout split_s390x_n_ns.stdout \
+	split_s390x_r.stdout
+SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
+split_s390_1_e1.o: split_s390_1_e1.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_e2.o: split_s390_1_e2.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_e3.o: split_s390_1_e3.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_e4.o: split_s390_1_e4.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z1.o: split_s390_1_z1.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z2.o: split_s390_1_z2.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z3.o: split_s390_1_z3.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z4.o: split_s390_1_z4.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_n.o: split_s390_1_n.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_a1.o: split_s390_1_a1.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_a2.o: split_s390_1_a2.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_2_s.o: split_s390_2_s.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_2_ns.o: split_s390_2_ns.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_e1: split_s390_1_e1.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e1.o split_s390_2_s.o
+split_s390_e1.stdout: split_s390_e1
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e2: split_s390_1_e2.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e2.o split_s390_2_s.o
+split_s390_e2.stdout: split_s390_e2
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e3: split_s390_1_e3.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e3.o split_s390_2_s.o
+split_s390_e3.stdout: split_s390_e3
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e4: split_s390_1_e4.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e4.o split_s390_2_s.o
+split_s390_e4.stdout: split_s390_e4
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_z1: split_s390_1_z1.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z1.o split_s390_2_s.o
+split_s390_z1.stdout: split_s390_z1
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z2: split_s390_1_z2.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z2.o split_s390_2_s.o
+split_s390_z2.stdout: split_s390_z2
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z3: split_s390_1_z3.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z3.o split_s390_2_s.o
+split_s390_z3.stdout: split_s390_z3
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z4: split_s390_1_z4.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z4.o split_s390_2_s.o
+split_s390_z4.stdout: split_s390_z4
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_n: split_s390_1_n.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_n.o split_s390_2_s.o
+split_s390_n.stdout: split_s390_n
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e1_ns: split_s390_1_e1.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e1.o split_s390_2_ns.o
+split_s390_e1_ns.stdout: split_s390_e1_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e2_ns: split_s390_1_e2.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e2.o split_s390_2_ns.o
+split_s390_e2_ns.stdout: split_s390_e2_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e3_ns: split_s390_1_e3.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e3.o split_s390_2_ns.o
+split_s390_e3_ns.stdout: split_s390_e3_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e4_ns: split_s390_1_e4.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e4.o split_s390_2_ns.o
+split_s390_e4_ns.stdout: split_s390_e4_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_z1_ns: split_s390_1_z1.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z1.o split_s390_2_ns.o
+split_s390_z1_ns.stdout: split_s390_z1_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z2_ns: split_s390_1_z2.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z2.o split_s390_2_ns.o
+split_s390_z2_ns.stdout: split_s390_z2_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z3_ns: split_s390_1_z3.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z3.o split_s390_2_ns.o
+split_s390_z3_ns.stdout: split_s390_z3_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z4_ns: split_s390_1_z4.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z4.o split_s390_2_ns.o
+split_s390_z4_ns.stdout: split_s390_z4_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_n_ns: split_s390_1_n.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_n.o split_s390_2_ns.o
+split_s390_n_ns.stdout: split_s390_n_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_a1.stdout: split_s390_1_a1.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o split_s390_a1 split_s390_1_a1.o split_s390_2_ns.o > $@ 2>&1 || exit 0
+split_s390_a2: split_s390_1_a2.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_a2.o split_s390_2_ns.o
+split_s390_a2.stdout: split_s390_a2
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_r.stdout: split_s390_1_z1.o split_s390_2_ns.o ../ld-new
+	../ld-new -r split_s390_1_z1.o split_s390_2_ns.o -o split_s390_r > $@ 2>&1 || exit 0
+split_s390x_1_z1.o: split_s390x_1_z1.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_z2.o: split_s390x_1_z2.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_z3.o: split_s390x_1_z3.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_z4.o: split_s390x_1_z4.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_n.o: split_s390x_1_n.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_a1.o: split_s390x_1_a1.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_a2.o: split_s390x_1_a2.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_2_s.o: split_s390x_2_s.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_2_ns.o: split_s390x_2_ns.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_z1: split_s390x_1_z1.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z1.o split_s390x_2_s.o
+split_s390x_z1.stdout: split_s390x_z1
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z2: split_s390x_1_z2.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z2.o split_s390x_2_s.o
+split_s390x_z2.stdout: split_s390x_z2
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z3: split_s390x_1_z3.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z3.o split_s390x_2_s.o
+split_s390x_z3.stdout: split_s390x_z3
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z4: split_s390x_1_z4.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z4.o split_s390x_2_s.o
+split_s390x_z4.stdout: split_s390x_z4
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_n: split_s390x_1_n.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_n.o split_s390x_2_s.o
+split_s390x_n.stdout: split_s390x_n
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390x_z1_ns: split_s390x_1_z1.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z1.o split_s390x_2_ns.o
+split_s390x_z1_ns.stdout: split_s390x_z1_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z2_ns: split_s390x_1_z2.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z2.o split_s390x_2_ns.o
+split_s390x_z2_ns.stdout: split_s390x_z2_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z3_ns: split_s390x_1_z3.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z3.o split_s390x_2_ns.o
+split_s390x_z3_ns.stdout: split_s390x_z3_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z4_ns: split_s390x_1_z4.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z4.o split_s390x_2_ns.o
+split_s390x_z4_ns.stdout: split_s390x_z4_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_n_ns: split_s390x_1_n.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_n.o split_s390x_2_ns.o
+split_s390x_n_ns.stdout: split_s390x_n_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390x_a1.stdout: split_s390x_1_a1.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o split_s390x_a1 split_s390x_1_a1.o split_s390x_2_ns.o > $@ 2>&1 || exit 0
+split_s390x_a2: split_s390x_1_a2.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_a2.o split_s390x_2_ns.o
+split_s390x_a2.stdout: split_s390x_a2
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390x_r.stdout: split_s390x_1_z1.o split_s390x_2_ns.o ../ld-new
+	../ld-new -r split_s390x_1_z1.o split_s390x_2_ns.o -o split_s390x_r > $@ 2>&1 || exit 0
+MOSTLYCLEANFILES += split_s390_1_e1 split_s390_1_e2 split_s390_1_e3 \
+	split_s390_1_e4 split_s390_1_z1 split_s390_1_z2 split_s390_1_z3 \
+	split_s390_1_z4 split_s390_1_n split_s390_1_a1 split_s390_1_a2 \
+	split_s390_1_e1_ns split_s390_1_e2_ns split_s390_1_e3_ns \
+	split_s390_1_e4_ns split_s390_1_z1_ns split_s390_1_z2_ns \
+	split_s390_1_z3_ns split_s390_1_z4_ns split_s390_1_n_ns \
+	split_s390_r split_s390x_1_z1 split_s390x_1_z2 split_s390x_1_z3 \
+	split_s390x_1_z4 split_s390x_1_n split_s390x_1_a1 split_s390x_1_a2 \
+	split_s390x_1_z1_ns split_s390x_1_z2_ns split_s390x_1_z3_ns \
+	split_s390x_1_z4_ns split_s390x_1_n_ns split_s390x_r
+
+endif DEFAULT_TARGET_S390
+
 endif NATIVE_OR_CROSS_LINKER
 
 # Tests for the dwp tool.
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index 31c9951..f7f8e09 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -816,10 +816,38 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	arm_farcall_thumb_thumb_6m \
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	arm_farcall_thumb_arm \
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	arm_farcall_thumb_arm_5t
-@DEFAULT_TARGET_X86_64_TRUE@am__append_88 = *.dwo *.dwp
-@DEFAULT_TARGET_X86_64_TRUE@am__append_89 = dwp_test_1.sh \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_88 = split_s390.sh
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_89 = split_s390_e1.stdout split_s390_e2.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_e3.stdout split_s390_e4.stdout split_s390_z1.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_z2.stdout split_s390_z3.stdout split_s390_z4.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_n.stdout split_s390_a1.stdout split_s390_a2.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_e1_ns.stdout split_s390_e2_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_e3_ns.stdout split_s390_e4_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_z1_ns.stdout split_s390_z2_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_z3_ns.stdout split_s390_z4_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_n_ns.stdout split_s390_r.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_z1.stdout split_s390x_z2.stdout split_s390x_z3.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_z4.stdout split_s390x_n.stdout split_s390x_a1.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_a2.stdout split_s390x_z1_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_z2_ns.stdout split_s390x_z3_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_z4_ns.stdout split_s390x_n_ns.stdout \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_r.stdout
+
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_90 = split_s390_1_e1 split_s390_1_e2 split_s390_1_e3 \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_1_e4 split_s390_1_z1 split_s390_1_z2 split_s390_1_z3 \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_1_z4 split_s390_1_n split_s390_1_a1 split_s390_1_a2 \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_1_e1_ns split_s390_1_e2_ns split_s390_1_e3_ns \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_1_e4_ns split_s390_1_z1_ns split_s390_1_z2_ns \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_1_z3_ns split_s390_1_z4_ns split_s390_1_n_ns \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390_r split_s390x_1_z1 split_s390x_1_z2 split_s390x_1_z3 \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_1_z4 split_s390x_1_n split_s390x_1_a1 split_s390x_1_a2 \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_1_z1_ns split_s390x_1_z2_ns split_s390x_1_z3_ns \
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_s390x_1_z4_ns split_s390x_1_n_ns split_s390x_r
+
+@DEFAULT_TARGET_X86_64_TRUE@am__append_91 = *.dwo *.dwp
+@DEFAULT_TARGET_X86_64_TRUE@am__append_92 = dwp_test_1.sh \
 @DEFAULT_TARGET_X86_64_TRUE@	dwp_test_2.sh
-@DEFAULT_TARGET_X86_64_TRUE@am__append_90 = dwp_test_1.stdout \
+@DEFAULT_TARGET_X86_64_TRUE@am__append_93 = dwp_test_1.stdout \
 @DEFAULT_TARGET_X86_64_TRUE@	dwp_test_2.stdout
 subdir = testsuite
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
@@ -2461,7 +2489,7 @@ MOSTLYCLEANFILES = *.so *.syms *.stdout $(am__append_4) \
 	$(am__append_45) $(am__append_51) $(am__append_67) \
 	$(am__append_70) $(am__append_72) $(am__append_75) \
 	$(am__append_78) $(am__append_81) $(am__append_84) \
-	$(am__append_87) $(am__append_88)
+	$(am__append_87) $(am__append_90) $(am__append_91)
 
 # We will add to these later, for each individual test.  Note
 # that we add each test under check_SCRIPTS or check_PROGRAMS;
@@ -2471,13 +2499,13 @@ check_SCRIPTS = $(am__append_2) $(am__append_20) $(am__append_26) \
 	$(am__append_42) $(am__append_46) $(am__append_49) \
 	$(am__append_65) $(am__append_68) $(am__append_73) \
 	$(am__append_76) $(am__append_79) $(am__append_82) \
-	$(am__append_85) $(am__append_89)
+	$(am__append_85) $(am__append_88) $(am__append_92)
 check_DATA = $(am__append_3) $(am__append_21) $(am__append_27) \
 	$(am__append_30) $(am__append_36) $(am__append_39) \
 	$(am__append_43) $(am__append_47) $(am__append_50) \
 	$(am__append_66) $(am__append_69) $(am__append_74) \
 	$(am__append_77) $(am__append_80) $(am__append_83) \
-	$(am__append_86) $(am__append_90)
+	$(am__append_86) $(am__append_89) $(am__append_93)
 BUILT_SOURCES = $(am__append_33)
 TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
 
@@ -2979,6 +3007,7 @@ LDADD = libgoldtest.a ../libgold.a ../../libiberty/libiberty.a $(LIBINTL) \
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_LDFLAGS = $(exception_test_LDFLAGS)
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_LDADD = exception_x86_64_bnd_1.o exception_x86_64_bnd_2.o
 @DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
 @DEFAULT_TARGET_X32_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
 @DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
 all: $(BUILT_SOURCES)
@@ -4540,6 +4569,8 @@ arm_farcall_thumb_thumb.sh.log: arm_farcall_thumb_thumb.sh
 	@p='arm_farcall_thumb_thumb.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 arm_farcall_thumb_arm.sh.log: arm_farcall_thumb_arm.sh
 	@p='arm_farcall_thumb_arm.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
+split_s390.sh.log: split_s390.sh
+	@p='split_s390.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 dwp_test_1.sh.log: dwp_test_1.sh
 	@p='dwp_test_1.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 dwp_test_2.sh.log: dwp_test_2.sh
@@ -6830,6 +6861,178 @@ uninstall-am:
 
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@arm_farcall_thumb_arm_5t.o: arm_farcall_thumb_arm.s
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -march=armv5t -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_e1.o: split_s390_1_e1.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_e2.o: split_s390_1_e2.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_e3.o: split_s390_1_e3.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_e4.o: split_s390_1_e4.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_z1.o: split_s390_1_z1.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_z2.o: split_s390_1_z2.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_z3.o: split_s390_1_z3.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_z4.o: split_s390_1_z4.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_n.o: split_s390_1_n.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_a1.o: split_s390_1_a1.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_1_a2.o: split_s390_1_a2.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_2_s.o: split_s390_2_s.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_2_ns.o: split_s390_2_ns.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m31 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e1: split_s390_1_e1.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e1.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e1.stdout: split_s390_e1
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e2: split_s390_1_e2.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e2.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e2.stdout: split_s390_e2
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e3: split_s390_1_e3.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e3.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e3.stdout: split_s390_e3
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e4: split_s390_1_e4.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e4.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e4.stdout: split_s390_e4
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z1: split_s390_1_z1.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z1.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z1.stdout: split_s390_z1
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z2: split_s390_1_z2.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z2.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z2.stdout: split_s390_z2
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z3: split_s390_1_z3.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z3.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z3.stdout: split_s390_z3
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z4: split_s390_1_z4.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z4.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z4.stdout: split_s390_z4
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_n: split_s390_1_n.o split_s390_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_n.o split_s390_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_n.stdout: split_s390_n
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e1_ns: split_s390_1_e1.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e1.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e1_ns.stdout: split_s390_e1_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e2_ns: split_s390_1_e2.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e2.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e2_ns.stdout: split_s390_e2_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e3_ns: split_s390_1_e3.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e3.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e3_ns.stdout: split_s390_e3_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e4_ns: split_s390_1_e4.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e4.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_e4_ns.stdout: split_s390_e4_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z1_ns: split_s390_1_z1.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z1.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z1_ns.stdout: split_s390_z1_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z2_ns: split_s390_1_z2.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z2.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z2_ns.stdout: split_s390_z2_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z3_ns: split_s390_1_z3.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z3.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z3_ns.stdout: split_s390_z3_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z4_ns: split_s390_1_z4.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z4.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_z4_ns.stdout: split_s390_z4_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_n_ns: split_s390_1_n.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_n.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_n_ns.stdout: split_s390_n_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_a1.stdout: split_s390_1_a1.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o split_s390_a1 split_s390_1_a1.o split_s390_2_ns.o > $@ 2>&1 || exit 0
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_a2: split_s390_1_a2.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_a2.o split_s390_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_a2.stdout: split_s390_a2
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390_r.stdout: split_s390_1_z1.o split_s390_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new -r split_s390_1_z1.o split_s390_2_ns.o -o split_s390_r > $@ 2>&1 || exit 0
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_z1.o: split_s390x_1_z1.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_z2.o: split_s390x_1_z2.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_z3.o: split_s390x_1_z3.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_z4.o: split_s390x_1_z4.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_n.o: split_s390x_1_n.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_a1.o: split_s390x_1_a1.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_1_a2.o: split_s390x_1_a2.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_2_s.o: split_s390x_2_s.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_2_ns.o: split_s390x_2_ns.s
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -m64 -o $@ $<
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z1: split_s390x_1_z1.o split_s390x_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z1.o split_s390x_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z1.stdout: split_s390x_z1
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z2: split_s390x_1_z2.o split_s390x_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z2.o split_s390x_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z2.stdout: split_s390x_z2
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z3: split_s390x_1_z3.o split_s390x_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z3.o split_s390x_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z3.stdout: split_s390x_z3
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z4: split_s390x_1_z4.o split_s390x_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z4.o split_s390x_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z4.stdout: split_s390x_z4
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_n: split_s390x_1_n.o split_s390x_2_s.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_n.o split_s390x_2_s.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_n.stdout: split_s390x_n
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z1_ns: split_s390x_1_z1.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z1.o split_s390x_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z1_ns.stdout: split_s390x_z1_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z2_ns: split_s390x_1_z2.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z2.o split_s390x_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z2_ns.stdout: split_s390x_z2_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z3_ns: split_s390x_1_z3.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z3.o split_s390x_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z3_ns.stdout: split_s390x_z3_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z4_ns: split_s390x_1_z4.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z4.o split_s390x_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_z4_ns.stdout: split_s390x_z4_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_n_ns: split_s390x_1_n.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_n.o split_s390x_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_n_ns.stdout: split_s390x_n_ns
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_a1.stdout: split_s390x_1_a1.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o split_s390x_a1 split_s390x_1_a1.o split_s390x_2_ns.o > $@ 2>&1 || exit 0
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_a2: split_s390x_1_a2.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_a2.o split_s390x_2_ns.o
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_a2.stdout: split_s390x_a2
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_s390x_r.stdout: split_s390x_1_z1.o split_s390x_2_ns.o ../ld-new
+@DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new -r split_s390x_1_z1.o split_s390x_2_ns.o -o split_s390x_r > $@ 2>&1 || exit 0
 
 # Tests for the dwp tool.
 # We don't want to rely yet on GCC support for -gsplit-dwarf,
diff --git a/gold/testsuite/split_s390.sh b/gold/testsuite/split_s390.sh
new file mode 100755
index 0000000..96add07
--- /dev/null
+++ b/gold/testsuite/split_s390.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+# split_s390.sh -- test -fstack-split for s390
+
+# Copyright (C) 2009-2015 Free Software Foundation, Inc.
+# Written by Marcin Kościelnicki <koriakin@0x04.net>.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+match()
+{
+  if ! egrep "$1" "$2" >/dev/null 2>&1; then
+    echo 1>&2 "could not find '$1' in $2"
+    exit 1
+  fi
+}
+
+nomatch()
+{
+  if egrep "$1" "$2" >/dev/null 2>&1; then
+    echo 1>&2 "found unexpected '$1' in $2"
+    exit 1
+  fi
+}
+
+match 'basr.*%r1,%r0$' split_s390_e1.stdout
+match 'long.*0x00100000$' split_s390_e1.stdout
+match 'basr.*%r1,%r0$' split_s390_e1_ns.stdout
+match 'long.*0x00104000$' split_s390_e1_ns.stdout
+
+match 'ear.*$' split_s390_e2.stdout
+match 'jhe.*$' split_s390_e2.stdout
+match 'basr.*%r1,%r0$' split_s390_e2.stdout
+match 'long.*0x00000100$' split_s390_e2.stdout
+nomatch 'ear.*$' split_s390_e2_ns.stdout
+nomatch 'jhe.*$' split_s390_e2_ns.stdout
+match 'basr.*%r1,%r0$' split_s390_e2_ns.stdout
+match 'long.*0x00004100$' split_s390_e2_ns.stdout
+
+match 'ear.*$' split_s390_e3.stdout
+match 'jl.*$' split_s390_e3.stdout
+nomatch 'j	.*$' split_s390_e3.stdout
+match 'basr.*%r1,%r0$' split_s390_e3.stdout
+match 'long.*0x00000100$' split_s390_e3.stdout
+nomatch 'ear.*$' split_s390_e3_ns.stdout
+nomatch 'jl.*$' split_s390_e3_ns.stdout
+match 'j	.*$' split_s390_e3_ns.stdout
+match 'basr.*%r1,%r0$' split_s390_e3_ns.stdout
+match 'long.*0x00004100$' split_s390_e3_ns.stdout
+
+match 'ear.*$' split_s390_e4.stdout
+match 'jl.*$' split_s390_e4.stdout
+nomatch 'j	.*$' split_s390_e4.stdout
+match 'basr.*%r1,%r0$' split_s390_e4.stdout
+match 'long.*0x00001000$' split_s390_e4.stdout
+nomatch 'ear.*$' split_s390_e4_ns.stdout
+nomatch 'jl.*$' split_s390_e4_ns.stdout
+match 'j	.*$' split_s390_e4_ns.stdout
+match 'basr.*%r1,%r0$' split_s390_e4_ns.stdout
+match 'long.*0x00005000$' split_s390_e4_ns.stdout
+
+match 'jg.*__morestack>?$' split_s390_z1.stdout
+match 'long.*0x00100000$' split_s390_z1.stdout
+match 'jg.*__morestack>?$' split_s390_z1_ns.stdout
+match 'long.*0x00104000$' split_s390_z1_ns.stdout
+
+match 'ear.*$' split_s390_z2.stdout
+match 'jgl.*__morestack>?$' split_s390_z2.stdout
+nomatch 'jg	.*__morestack>?$' split_s390_z2.stdout
+match 'long.*0x00000100$' split_s390_z2.stdout
+nomatch 'ear.*$' split_s390_z2_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390_z2_ns.stdout
+match 'jg	.*__morestack>?$' split_s390_z2_ns.stdout
+match 'long.*0x00004100$' split_s390_z2_ns.stdout
+
+match 'ear.*$' split_s390_z3.stdout
+match 'jgl.*__morestack>?$' split_s390_z3.stdout
+nomatch 'jg	.*__morestack>?$' split_s390_z3.stdout
+match 'long.*0x00001000$' split_s390_z3.stdout
+nomatch 'ear.*$' split_s390_z3_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390_z3_ns.stdout
+match 'jg	.*__morestack>?$' split_s390_z3_ns.stdout
+match 'long.*0x00005000$' split_s390_z3_ns.stdout
+
+match 'alfi.*%r1,1048576$' split_s390_z4.stdout
+match 'jgl.*__morestack>?$' split_s390_z4.stdout
+match 'long.*0x00100000$' split_s390_z4.stdout
+match 'alfi.*%r1,1064960$' split_s390_z4_ns.stdout
+match 'jgl.*__morestack>?$' split_s390_z4_ns.stdout
+match 'long.*0x00104000$' split_s390_z4_ns.stdout
+
+match 'jg.*__morestack>?$' split_s390x_z1.stdout
+match 'long.*0x00100000$' split_s390x_z1.stdout
+match 'jg.*__morestack>?$' split_s390x_z1_ns.stdout
+match 'long.*0x00104000$' split_s390x_z1_ns.stdout
+
+match 'ear.*$' split_s390x_z2.stdout
+match 'jgl.*__morestack>?$' split_s390x_z2.stdout
+nomatch 'jg	.*__morestack>?$' split_s390x_z2.stdout
+match 'long.*0x00000100$' split_s390x_z2.stdout
+nomatch 'ear.*$' split_s390x_z2_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390x_z2_ns.stdout
+match 'jg	.*__morestack>?$' split_s390x_z2_ns.stdout
+match 'long.*0x00004100$' split_s390x_z2_ns.stdout
+
+match 'ear.*$' split_s390x_z3.stdout
+match 'jgl.*__morestack>?$' split_s390x_z3.stdout
+nomatch 'jg	.*__morestack>?$' split_s390x_z3.stdout
+match 'long.*0x00001000$' split_s390x_z3.stdout
+nomatch 'ear.*$' split_s390x_z3_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390x_z3_ns.stdout
+match 'jg	.*__morestack>?$' split_s390x_z3_ns.stdout
+match 'long.*0x00005000$' split_s390x_z3_ns.stdout
+
+match 'algfi.*%r1,1048576$' split_s390x_z4.stdout
+match 'jgl.*__morestack>?$' split_s390x_z4.stdout
+match 'long.*0x00100000$' split_s390x_z4.stdout
+match 'algfi.*%r1,1064960$' split_s390x_z4_ns.stdout
+match 'jgl.*__morestack>?$' split_s390x_z4_ns.stdout
+match 'long.*0x00104000$' split_s390x_z4_ns.stdout
+
+match 'nopr.*r15$' split_s390_n.stdout
+match 'nopr.*r15$' split_s390_n_ns.stdout
+match 'nopr.*r15$' split_s390x_n.stdout
+match 'nopr.*r15$' split_s390x_n_ns.stdout
+
+match 'failed to match' split_s390_a1.stdout
+match 'failed to match' split_s390x_a1.stdout
+
+match 'bas.*$' split_s390_a2.stdout
+match 'brasl.*__morestack>?$' split_s390x_a2.stdout
+
+match 'cannot mix' split_s390_r.stdout
+match 'cannot mix' split_s390x_r.stdout
diff --git a/gold/testsuite/split_s390_1_a1.s b/gold/testsuite/split_s390_1_a1.s
new file mode 100644
index 0000000..86c1997
--- /dev/null
+++ b/gold/testsuite/split_s390_1_a1.s
@@ -0,0 +1,36 @@
+# split_s390_1_a1.s: s390 specific, adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L1:
+	l	%r1, .L2-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	l	%r1, .L3-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L2:
+	.long	__morestack-.L1
+.L3:
+	.long	fn2-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_a2.s b/gold/testsuite/split_s390_1_a2.s
new file mode 100644
index 0000000..026752f
--- /dev/null
+++ b/gold/testsuite/split_s390_1_a2.s
@@ -0,0 +1,37 @@
+# split_s390_1_a2.s: s390 specific, permitted adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L1:
+	l	%r1, .L2-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	l	%r1, .L3-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L2:
+	.long	__morestack-.L1
+.L3:
+	.long	fn2-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
+	.section	.note.GNU-no-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e1.s b/gold/testsuite/split_s390_1_e1.s
new file mode 100644
index 0000000..354dc91
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e1.s
@@ -0,0 +1,45 @@
+# split_s390_e1.s: s390 specific test case for -fsplit-stack -
+# esa mode, unconditional call.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x100000
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e2.s b/gold/testsuite/split_s390_1_e2.s
new file mode 100644
index 0000000..af802fa
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e2.s
@@ -0,0 +1,48 @@
+# split_s390_e2.s: s390 specific test case for -fsplit-stack -
+# esa mode, jhe conditional call, no add.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	c	%r15, 0x20(%r1)
+	jhe	.L4
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x100
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e3.s b/gold/testsuite/split_s390_1_e3.s
new file mode 100644
index 0000000..107419b
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e3.s
@@ -0,0 +1,49 @@
+# split_s390_e3.s: s390 specific test case for -fsplit-stack -
+# esa mode, jl conditional call, no add.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	c	%r15, 0x20(%r1)
+	jl	.L7
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+.L7:
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x100
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e4.s b/gold/testsuite/split_s390_1_e4.s
new file mode 100644
index 0000000..0687682
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e4.s
@@ -0,0 +1,51 @@
+# split_s390_e4.s: s390 specific test case for -fsplit-stack -
+# esa mode, jl conditional call, ahi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	l	%r1, 0x20(%r1)
+	ahi	%r1, 0x1000
+	cr	%r15, %r1
+	jl	.L7
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+.L7:
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x1000
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_n.s b/gold/testsuite/split_s390_1_n.s
new file mode 100644
index 0000000..ca91262
--- /dev/null
+++ b/gold/testsuite/split_s390_1_n.s
@@ -0,0 +1,18 @@
+# split_s390_n.s: s390 specific test case for -fsplit-stack -
+# no stack frame
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	nopr	%r15
+	larl	%r2, fn2
+	br	%r14
+	.cfi_endproc
+
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_z1.s b/gold/testsuite/split_s390_1_z1.s
new file mode 100644
index 0000000..42adf55
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z1.s
@@ -0,0 +1,37 @@
+# split_s390_z3.s: s390 specific test case for -fsplit-stack -
+# zarch mode, unconditional call
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	larl	%r1, .L1
+	jg	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x100000
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_z2.s b/gold/testsuite/split_s390_1_z2.s
new file mode 100644
index 0000000..2717a83
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z2.s
@@ -0,0 +1,40 @@
+# split_s390_z2.s: s390 specific test case for -fsplit-stack -
+# zarch mode, conditional call, no add
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	c	%r15, 0x20(%r1)
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x100
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
+
diff --git a/gold/testsuite/split_s390_1_z3.s b/gold/testsuite/split_s390_1_z3.s
new file mode 100644
index 0000000..8f0cb54
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z3.s
@@ -0,0 +1,41 @@
+# split_s390_z3.s: s390 specific test case for -fsplit-stack -
+# zarch mode, conditional call, ahi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	l	%r1, 0x20(%r1)
+	ahi	%r1, 0x1000
+	cr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x1000
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_z4.s b/gold/testsuite/split_s390_1_z4.s
new file mode 100644
index 0000000..b80bf40
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z4.s
@@ -0,0 +1,41 @@
+# split_s390_z4.s: s390 specific test case for -fsplit-stack -
+# zarch mode, conditional call, alfi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	l	%r1, 0x20(%r1)
+	alfi	%r1, 0x100000
+	cr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x100000
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_2_ns.s b/gold/testsuite/split_s390_2_ns.s
new file mode 100644
index 0000000..fb5a993
--- /dev/null
+++ b/gold/testsuite/split_s390_2_ns.s
@@ -0,0 +1,12 @@
+# split_s390_2_ns.s: s390 specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_2_s.s b/gold/testsuite/split_s390_2_s.s
new file mode 100644
index 0000000..3df8009
--- /dev/null
+++ b/gold/testsuite/split_s390_2_s.s
@@ -0,0 +1,13 @@
+# split_s390_2_s.s: s390 specific, -fsplit-stack calling -fsplit-stack
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_a1.s b/gold/testsuite/split_s390x_1_a1.s
new file mode 100644
index 0000000..ccc62cb
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_a1.s
@@ -0,0 +1,27 @@
+# split_s390x_1_a1.s: s390x specific, adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, __morestack
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_a2.s b/gold/testsuite/split_s390x_1_a2.s
new file mode 100644
index 0000000..a5c067b
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_a2.s
@@ -0,0 +1,28 @@
+# split_s390x_1_a2.s: s390x specific, permitted adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, __morestack
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
+	.section	.note.GNU-no-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_n.s b/gold/testsuite/split_s390x_1_n.s
new file mode 100644
index 0000000..6e434b8
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_n.s
@@ -0,0 +1,17 @@
+# split_s390x_1_n.s: s390x specific test case for -fsplit-stack -
+# no stack frame.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	nopr	%r15
+	larl	%r2, fn2
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z1.s b/gold/testsuite/split_s390x_1_z1.s
new file mode 100644
index 0000000..4af34cd
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z1.s
@@ -0,0 +1,37 @@
+# split_s390x_1_z1.s: s390x specific test case for -fsplit-stack -
+# unconditional call.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	larl	%r1, .L1
+	jg	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x100000
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z2.s b/gold/testsuite/split_s390x_1_z2.s
new file mode 100644
index 0000000..a184f77
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z2.s
@@ -0,0 +1,41 @@
+# split_s390x_1_z2.s: s390x specific test case for -fsplit-stack -
+# conditional call, no add.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	sllg	%r1, %r1, 32
+	ear	%r1, %a1
+	cg	%r15, 0x38(%r1)
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x100
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z3.s b/gold/testsuite/split_s390x_1_z3.s
new file mode 100644
index 0000000..b0a4b0b
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z3.s
@@ -0,0 +1,43 @@
+# split_s390x_1_z3.s: s390x specific test case for -fsplit-stack -
+# conditional call, ahi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	sllg	%r1, %r1, 32
+	ear	%r1, %a1
+	lg	%r1, 0x38(%r1)
+	aghi	%r1, 0x1000
+	cgr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x1000
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z4.s b/gold/testsuite/split_s390x_1_z4.s
new file mode 100644
index 0000000..a3b942e
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z4.s
@@ -0,0 +1,43 @@
+# split_s390x_1_z4.s: s390x specific test case for -fsplit-stack -
+# conditional call, alfi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	sllg	%r1, %r1, 32
+	ear	%r1, %a1
+	lg	%r1, 0x38(%r1)
+	algfi	%r1, 0x100000
+	cgr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x100000
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_2_ns.s b/gold/testsuite/split_s390x_2_ns.s
new file mode 100644
index 0000000..f073eee
--- /dev/null
+++ b/gold/testsuite/split_s390x_2_ns.s
@@ -0,0 +1,12 @@
+# split_s390x_2_ns.s: s390x specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_2_s.s b/gold/testsuite/split_s390x_2_s.s
new file mode 100644
index 0000000..787d2fe
--- /dev/null
+++ b/gold/testsuite/split_s390x_2_s.s
@@ -0,0 +1,13 @@
+# split_s390x_2_s.s: s390x specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index 12c6552..1ae3cb6 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -557,12 +557,15 @@ class Target_x86_64 : public Sized_target<size, false>
   uint64_t
   do_ehframe_datarel_base() const;
 
+  typedef typename Sized_relobj_file<size, false>::Views Views;
+
   // 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;
+		     std::string* from, std::string* to,
+		     const unsigned char *, size_t, Views *) const;
 
   // Return the size of the GOT section.
   section_size_type
@@ -4600,7 +4603,10 @@ Target_x86_64<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
 					unsigned char* view,
 					section_size_type view_size,
 					std::string* from,
-					std::string* to) const
+					std::string* to,
+					const unsigned char *,
+					size_t,
+					Views *) const
 {
   const char* const cmp_insn = reinterpret_cast<const char*>
       (size == 32 ? cmp_insn_32 : cmp_insn_64);
-- 
2.6.3

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-09 15:15 [PATCH] [RFC] [GOLD] s390 -fsplit-stack support Marcin Kościelnicki
@ 2015-12-10  6:25 ` Cary Coutant
  2015-12-11 21:10   ` Cary Coutant
  2016-03-17 22:58   ` Cary Coutant
  0 siblings, 2 replies; 11+ messages in thread
From: Cary Coutant @ 2015-12-10  6:25 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

> The problem here is that I have to get to the proper place in .rodata
> (and mutate it), starting from the text section by parsing the
> relocation of the larl instruction aiming at it.  The parameters
> currently passed into do_calls_non_split unfortunately don't seem to
> allow this.

For the relocations, the Track_relocs class in reloc.h can simplify
the relocation tracking that you're doing. The initialize() method
requires an Object* and a section index. Unfortunately, we only have
the shndx of the data section, so we could also pass down the
reloc_shndx from do_relocate_sections, but Track_relocs::initialize()
is going to duplicate some work we've already done, so it would
probably make more sense to do what you've done and pass prelocs and
reloc_count down. We can add an overload for
Track_relocs::initialize() to bypass the overhead of fetching the
relocation section contents, like this:

template<int size, bool big_endian>
bool
Track_relocs<size, big_endian>::initialize(
    const unsigned char* prelocs,
    size_t reloc_count,
    unsigned int reloc_type)
{
  this->prelocs_ = prelocs;

  if (reloc_type == elfcpp::SHT_REL)
    this->reloc_size_ = elfcpp::Elf_sizes<size>::rel_size;
  else if (reloc_type == elfcpp::SHT_RELA)
    this->reloc_size_ = elfcpp::Elf_sizes<size>::rela_size;
  else
    gold_unreachable();

  this->len_ = this->reloc_size_ * reloc_count;
  return true;
}

That will allow you to replace this loop:

+      // Find out larl's operand.  It should be a local symbol in .rodata
+      // section.
+      const unsigned char *pr = prelocs;
+      for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size)
+        {
...
+        }

with something like this:

       Track_relocs<size, big_endian> track_relocs;
       track_relocs.initialize(prelocs, reloc_count, elfcpp::SHT_RELA);
       track_relocs.advance(curoffset);
       ...

To get the view of the section containing the parameter block, I'd
suggest adding a new Object method:

[public:]
const unsigned char*
Object::get_output_view(Output_file* of, unsigned int shndx,
section_size_type* plen)
{ return this->do_get_output_view(of, shndx, plen); }

[protected:]
virtual const unsigned char*
Object::do_get_output_view(Output_file* of, unsigned int shndx,
  section_size_type* plen)
{ gold_unreachable(); }

const unsigned char*
Sized_relobj_file<size, big_endian>::do_get_output_view(
    Output_file* of,
    unsigned int shndx,
    section_size_type* plen)
{
  const Output_section* os = out_sections[shndx];
  if (os == NULL)
    return NULL;
  Address output_offset = out_offsets[shndx];

  // Assume we don't have to support post-processed sections.
  gold_assert(!os->requires_postprocessing());

  off_t output_section_offset = os->offset();
  gold_assert(output_section_offset != invalid_address);

  // Rather than look up the section header to fetch the sh_size field
  // of the input section, we'll punt and just return a view that extends
  // to the end of the output section.
  section_size_type view_size =
      convert_to_section_size_type(os->data_size() - output_offset);

  // Assume we don't have to support compressed sections.
  section_size_type uncompressed_size;
  gold_assert(!this->section_is_compressed(shndx, &uncompressed_size));

  *plen = view_size;
  return of->get_output_view(output_section_offset + output_offset, view_size);
}

To use this method, we'll need the Output_file* pointer, so we'll need
to pass that down from do_relocate_sections() as well.

I think it's reasonable to expect that a target implementation of
do_calls_non_split may need prelocs, reloc_count, and Output_file*, so
I don't have a problem adding those to the signatures of
Sized_relobj_file::split_stack_adjust,
Sized_relobj_file::split_stack_adjust_reltype,
Target::calls_non_split, and Target::do_calls_non_split.

> I have managed to get it working, but only by making a lot of private
> or protected things public, and changing some types.  In summary:
>
> - the private Views class used in reloc.cc is made public and routed
>   to do_calls_non_split.
> - Relobj's output_sections method is likewise made public.

With the changes suggested above, these should not be necessary.

> - text section's relocation pointer and count are also routed to
>   do_calls_non_split.

This is OK (along with the Output_file* parameter).

> - since calls_non_split now takes a templated-type parameter (the reloc
>   pointer), it's moved to Sized_target class, and a few ugly dynamic
>   casts are inserted.

This should not be necessary. We don't use dynamic casts anywhere in
gold, so let's keep it that way.

> diff --git a/configure b/configure
> index 3bb1c03..91fd098 100755
> --- a/configure
> +++ b/configure
> @@ -2972,7 +2972,7 @@ case "${ENABLE_GOLD}" in
>        # Check for target supported by gold.
>        case "${target}" in
>          i?86-*-* | x86_64-*-* | sparc*-*-* | powerpc*-*-* | arm*-*-* \
> -        | aarch64*-*-* | tilegx*-*-*)
> +        | aarch64*-*-* | tilegx*-*-* | s390*-*-*)
>    configdirs="$configdirs gold"
>    if test x${ENABLE_GOLD} = xdefault; then
>      default_ld=gold

Please remove this change from the patch. I'll get the top-level
configure changes in separately for s390 and mips.

> diff --git a/gold/Makefile.in b/gold/Makefile.in

It's not necessary to include regenerated files in the patch you send.

+      if (static_cast<section_size_type>(curoffset + 2) > view_size)

Use convert_to_section_size_type().

+  // A function for targets to call.  Return whether BYTES/LEN matches
+  // VIEW/VIEW_SIZE at OFFSET.
+  bool
+  match_view(const unsigned char* view, section_size_type view_size,
+     section_offset_type offset, const unsigned char* bytes, size_t len) const
+    {
+      return this->match_view(view, view_size, offset,
+      reinterpret_cast<const char *>(bytes), len);
+    }

Rather than create two copies of the same routine, one of which calls
the other, I suggest making this version of match_view() an inline
private to the Target_s390 class.

Should be <const char*> (no space before *), and watch the line lengths.

Don't forget a ChangeLog entry!

-cary

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-10  6:25 ` Cary Coutant
@ 2015-12-11 21:10   ` Cary Coutant
  2015-12-11 21:30     ` Marcin Kościelnicki
                       ` (2 more replies)
  2016-03-17 22:58   ` Cary Coutant
  1 sibling, 3 replies; 11+ messages in thread
From: Cary Coutant @ 2015-12-11 21:10 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

> To get the view of the section containing the parameter block, I'd
> suggest adding a new Object method:
>
> [public:]
> const unsigned char*
> Object::get_output_view(Output_file* of, unsigned int shndx,
> section_size_type* plen)
> { return this->do_get_output_view(of, shndx, plen); }
> ...

After thinking about this a bit, I'd prefer to save a pointer to the
output views in the Sized_relobj object, so get_output_view() can
access the pre-computed view directly, rather than trying to recompute
it. I've added a simple RAII class to make sure the saved pointer gets
NULL'd when the output views go out of scope.

Try the attached patch. If this works for you, I'll go ahead and
commit it as preparation for your patch.

> I think it's reasonable to expect that a target implementation of
> do_calls_non_split may need prelocs, reloc_count, and Output_file*, so
> I don't have a problem adding those to the signatures of
> Sized_relobj_file::split_stack_adjust,
> Sized_relobj_file::split_stack_adjust_reltype,
> Target::calls_non_split, and Target::do_calls_non_split.

With this patch, we won't have to add the Output_file pointer to these
function signatures.

-cary


2015-12-11  Cary Coutant  <ccoutant@gmail.com>

gold/
        * object.cc (Sized_relobj_file::Sized_relobj_file): Initialize
        output_views_.
        * object.h (Object::get_output_view): New function.
        (Object::do_get_output_view): New function.
        (Sized_relobj_file::do_get_output_view): New function.
        (Sized_relobj_file::output_views_): New data member.
        * reloc.cc: (Sized_relobj_file::do_relocate): Store pointer to
        output views in class object.
        (Sized_relobj_file::do_get_output_view): New function.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-11 21:10   ` Cary Coutant
@ 2015-12-11 21:30     ` Marcin Kościelnicki
  2015-12-11 21:58       ` Cary Coutant
  2015-12-11 22:25     ` Cary Coutant
  2015-12-11 22:27     ` Cary Coutant
  2 siblings, 1 reply; 11+ messages in thread
From: Marcin Kościelnicki @ 2015-12-11 21:30 UTC (permalink / raw)
  To: Cary Coutant; +Cc: Binutils

On 11/12/15 22:10, Cary Coutant wrote:
>> To get the view of the section containing the parameter block, I'd
>> suggest adding a new Object method:
>>
>> [public:]
>> const unsigned char*
>> Object::get_output_view(Output_file* of, unsigned int shndx,
>> section_size_type* plen)
>> { return this->do_get_output_view(of, shndx, plen); }
>> ...
>
> After thinking about this a bit, I'd prefer to save a pointer to the
> output views in the Sized_relobj object, so get_output_view() can
> access the pre-computed view directly, rather than trying to recompute
> it. I've added a simple RAII class to make sure the saved pointer gets
> NULL'd when the output views go out of scope.
>
> Try the attached patch. If this works for you, I'll go ahead and
> commit it as preparation for your patch.
>

Thanks for the idea, but I don't see any attachment...

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-11 21:30     ` Marcin Kościelnicki
@ 2015-12-11 21:58       ` Cary Coutant
  0 siblings, 0 replies; 11+ messages in thread
From: Cary Coutant @ 2015-12-11 21:58 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

[-- Attachment #1: Type: text/plain, Size: 1014 bytes --]

Oops, sorry!

-cary

On Fri, Dec 11, 2015 at 1:30 PM, Marcin Kościelnicki <koriakin@0x04.net> wrote:
> On 11/12/15 22:10, Cary Coutant wrote:
>>>
>>> To get the view of the section containing the parameter block, I'd
>>> suggest adding a new Object method:
>>>
>>> [public:]
>>> const unsigned char*
>>> Object::get_output_view(Output_file* of, unsigned int shndx,
>>> section_size_type* plen)
>>> { return this->do_get_output_view(of, shndx, plen); }
>>> ...
>>
>>
>> After thinking about this a bit, I'd prefer to save a pointer to the
>> output views in the Sized_relobj object, so get_output_view() can
>> access the pre-computed view directly, rather than trying to recompute
>> it. I've added a simple RAII class to make sure the saved pointer gets
>> NULL'd when the output views go out of scope.
>>
>> Try the attached patch. If this works for you, I'll go ahead and
>> commit it as preparation for your patch.
>>
>
> Thanks for the idea, but I don't see any attachment...
>

[-- Attachment #2: output_views.patch --]
[-- Type: application/octet-stream, Size: 5602 bytes --]

2015-12-11  Cary Coutant  <ccoutant@gmail.com>

gold/
	* object.cc (Sized_relobj_file::Sized_relobj_file): Initialize
	output_views_.
	* object.h (Object::get_output_view): New function.
	(Object::do_get_output_view): New function.
	(Sized_relobj_file::do_get_output_view): New function.
	(Sized_relobj_file::output_views_): New data member.	
	* reloc.cc: (Sized_relobj_file::do_relocate): Store pointer to
	output views in class object.
	(Sized_relobj_file::do_get_output_view): New function.


diff --git a/gold/object.cc b/gold/object.cc
index 54b76bd..c1947bc 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -479,7 +479,8 @@ Sized_relobj_file<size, big_endian>::Sized_relobj_file(
     discarded_eh_frame_shndx_(-1U),
     is_deferred_layout_(false),
     deferred_layout_(),
-    deferred_layout_relocs_()
+    deferred_layout_relocs_(),
+    output_views_(NULL)
 {
   this->e_type_ = ehdr.get_e_type();
 }
diff --git a/gold/object.h b/gold/object.h
index f408408..6cb82c7 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -844,6 +844,11 @@ class Object
   get_incremental_reloc_count(unsigned int symndx) const
   { return this->do_get_incremental_reloc_count(symndx); }
 
+  // Return the output view for section SHNDX.
+  const unsigned char*
+  get_output_view(unsigned int shndx, section_size_type* plen) const
+  { return this->do_get_output_view(shndx, plen); }
+
  protected:
   // Returns NULL for Objects that are not dynamic objects.  This method
   // is overridden in the Dynobj class.
@@ -1029,6 +1034,11 @@ class Object
   do_get_incremental_reloc_count(unsigned int) const
   { gold_unreachable(); }
 
+  // Return the output view for a section.
+  virtual const unsigned char*
+  do_get_output_view(unsigned int, section_size_type*) const
+  { gold_unreachable(); }
+
   void
   set_compressed_sections(Compressed_section_map* compressed_sections)
   { this->compressed_sections_ = compressed_sections; }
@@ -2563,6 +2573,10 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
   set_output_local_symbol_count(unsigned int value)
   { this->output_local_symbol_count_ = value; }
 
+  // Return the output view for a section.
+  const unsigned char*
+  do_get_output_view(unsigned int, section_size_type*) const;
+
  private:
   // For convenience.
   typedef Sized_relobj_file<size, big_endian> This;
@@ -2829,6 +2843,8 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
   std::vector<Deferred_layout> deferred_layout_;
   // The list of relocation sections whose layout was deferred.
   std::vector<Deferred_layout> deferred_layout_relocs_;
+  // Pointer to the list of output views; valid only during do_relocate().
+  const Views* output_views_;
 };
 
 // A class to manage the list of all objects.
diff --git a/gold/reloc.cc b/gold/reloc.cc
index f18f432..b83a962 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -665,6 +665,24 @@ Sized_relobj_file<size, big_endian>::do_relocate(const Symbol_table* symtab,
   // input offsets to output addresses.
   this->initialize_input_to_output_maps();
 
+  // Make the views available through get_output_view() for the duration
+  // of this routine.  This RAII class will reset output_views_ to NULL
+  // when the views go out of scope.
+  struct Set_output_views
+  {
+    Set_output_views(const Views** ppviews, const Views* pviews)
+    {
+      ppviews_ = ppviews;
+      *ppviews = pviews;
+    }
+
+    ~Set_output_views()
+    { *ppviews_ = NULL; }
+
+    const Views** ppviews_;
+  };
+  Set_output_views set_output_views(&this->output_views_, &views);
+
   // Apply relocations.
 
   this->relocate_sections(symtab, layout, pshdrs, of, &views);
@@ -1040,6 +1058,21 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections(
     }
 }
 
+// Return the output view for section SHNDX.
+
+template<int size, bool big_endian>
+const unsigned char*
+Sized_relobj_file<size, big_endian>::do_get_output_view(
+    unsigned int shndx,
+    section_size_type* plen) const
+{
+  gold_assert(this->output_views_ != NULL);
+  gold_assert(shndx < this->output_views_->size());
+  const View_size& v = (*this->output_views_)[shndx];
+  *plen = v.view_size;
+  return v.view;
+}
+
 // Write the incremental relocs.
 
 template<int size, bool big_endian>
@@ -1739,6 +1772,12 @@ Sized_relobj_file<32, false>::do_relocate_sections(
     const unsigned char* pshdrs,
     Output_file* of,
     Views* pviews);
+
+template
+const unsigned char*
+Sized_relobj_file<32, false>::do_get_output_view(
+    unsigned int shndx,
+    section_size_type* plen) const;
 #endif
 
 #ifdef HAVE_TARGET_32_BIG
@@ -1750,6 +1789,12 @@ Sized_relobj_file<32, true>::do_relocate_sections(
     const unsigned char* pshdrs,
     Output_file* of,
     Views* pviews);
+
+template
+const unsigned char*
+Sized_relobj_file<32, true>::do_get_output_view(
+    unsigned int shndx,
+    section_size_type* plen) const;
 #endif
 
 #ifdef HAVE_TARGET_64_LITTLE
@@ -1761,6 +1806,12 @@ Sized_relobj_file<64, false>::do_relocate_sections(
     const unsigned char* pshdrs,
     Output_file* of,
     Views* pviews);
+
+template
+const unsigned char*
+Sized_relobj_file<64, false>::do_get_output_view(
+    unsigned int shndx,
+    section_size_type* plen) const;
 #endif
 
 #ifdef HAVE_TARGET_64_BIG
@@ -1772,6 +1823,12 @@ Sized_relobj_file<64, true>::do_relocate_sections(
     const unsigned char* pshdrs,
     Output_file* of,
     Views* pviews);
+
+template
+const unsigned char*
+Sized_relobj_file<64, true>::do_get_output_view(
+    unsigned int shndx,
+    section_size_type* plen) const;
 #endif
 
 #ifdef HAVE_TARGET_32_LITTLE

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-11 21:10   ` Cary Coutant
  2015-12-11 21:30     ` Marcin Kościelnicki
@ 2015-12-11 22:25     ` Cary Coutant
  2015-12-11 22:27     ` Cary Coutant
  2 siblings, 0 replies; 11+ messages in thread
From: Cary Coutant @ 2015-12-11 22:25 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

> 2015-12-11  Cary Coutant  <ccoutant@gmail.com>
>
> gold/
>         * object.cc (Sized_relobj_file::Sized_relobj_file): Initialize
>         output_views_.
>         * object.h (Object::get_output_view): New function.
>         (Object::do_get_output_view): New function.
>         (Sized_relobj_file::do_get_output_view): New function.
>         (Sized_relobj_file::output_views_): New data member.
>         * reloc.cc: (Sized_relobj_file::do_relocate): Store pointer to
>         output views in class object.
>         (Sized_relobj_file::do_get_output_view): New function.

I've committed this patch.

-cary

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-11 21:10   ` Cary Coutant
  2015-12-11 21:30     ` Marcin Kościelnicki
  2015-12-11 22:25     ` Cary Coutant
@ 2015-12-11 22:27     ` Cary Coutant
  2015-12-13  2:09       ` Marcin Kościelnicki
  2 siblings, 1 reply; 11+ messages in thread
From: Cary Coutant @ 2015-12-11 22:27 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

[-- Attachment #1: Type: text/plain, Size: 1206 bytes --]

>> I think it's reasonable to expect that a target implementation of
>> do_calls_non_split may need prelocs, reloc_count, and Output_file*, so
>> I don't have a problem adding those to the signatures of
>> Sized_relobj_file::split_stack_adjust,
>> Sized_relobj_file::split_stack_adjust_reltype,
>> Target::calls_non_split, and Target::do_calls_non_split.
>
> With this patch, we won't have to add the Output_file pointer to these
> function signatures.

I've also committed the attached patch to add prelocs and reloc_count
parameters to Target::calls_non_split and Target::do_calls_non_split.

-cary


2015-12-11  Cary Coutant  <ccoutant@gmail.com>

gold/
        * target.h (Target::calls_non_split): Add prelocs, reloc_count
        parameters.
        (Target::do_calls_non_split): Likewise.
        * target.cc (Target::do_calls_non_split): Likewise.
        * reloc.cc (Sized_relobj_file::split_stack_adjust_reltype): Adjust
        call to Target::calls_non_split.
        * i386.cc (Target_i386::do_calls_non_split): Add prelocs, reloc_count
        parameters.
        * powerpc.cc (Target_powerpc::do_calls_non_split): Likewise.
        * x86_64.cc (Target_x86_64::do_calls_non_split): Likewise.

[-- Attachment #2: calls-non-split-prelocs.patch --]
[-- Type: application/octet-stream, Size: 5349 bytes --]

Pass relocations to Target::do_calls_non_split.

2015-12-11  Cary Coutant  <ccoutant@gmail.com>

gold/
	* target.h (Target::calls_non_split): Add prelocs, reloc_count
	parameters.
	(Target::do_calls_non_split): Likewise.
	* target.cc (Target::do_calls_non_split): Likewise.
	* reloc.cc (Sized_relobj_file::split_stack_adjust_reltype): Adjust
	call to Target::calls_non_split.
	* i386.cc (Target_i386::do_calls_non_split): Add prelocs, reloc_count
	parameters.
	* powerpc.cc (Target_powerpc::do_calls_non_split): Likewise.
	* x86_64.cc (Target_x86_64::do_calls_non_split): Likewise.


diff --git a/gold/i386.cc b/gold/i386.cc
index 8cc7244..e0809a4 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -491,6 +491,7 @@ class Target_i386 : public Sized_target<32, false>
   void
   do_calls_non_split(Relobj* object, unsigned int shndx,
 		     section_offset_type fnoffset, section_size_type fnsize,
+		     const unsigned char* prelocs, size_t reloc_count,
 		     unsigned char* view, section_size_type view_size,
 		     std::string* from, std::string* to) const;
 
@@ -3881,6 +3882,8 @@ void
 Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx,
 				       section_offset_type fnoffset,
 				       section_size_type fnsize,
+				       const unsigned char*,
+				       size_t,
 				       unsigned char* view,
 				       section_size_type view_size,
 				       std::string* from,
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 59d04b3..71e38f9 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -631,6 +631,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
   void
   do_calls_non_split(Relobj* object, unsigned int shndx,
 		     section_offset_type fnoffset, section_size_type fnsize,
+		     const unsigned char* prelocs, size_t reloc_count,
 		     unsigned char* view, section_size_type view_size,
 		     std::string* from, std::string* to) const;
 
@@ -6590,6 +6591,8 @@ Target_powerpc<size, big_endian>::do_calls_non_split(
     unsigned int shndx,
     section_offset_type fnoffset,
     section_size_type fnsize,
+    const unsigned char* prelocs,
+    size_t reloc_count,
     unsigned char* view,
     section_size_type view_size,
     std::string* from,
@@ -6600,7 +6603,8 @@ Target_powerpc<size, big_endian>::do_calls_non_split(
     {
       // warn
       Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
-				 view, view_size, from, to);
+				 prelocs, reloc_count, view, view_size,
+				 from, to);
       return;
     }
 
diff --git a/gold/reloc.cc b/gold/reloc.cc
index b83a962..c0c06c5 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -1364,6 +1364,7 @@ Sized_relobj_file<size, big_endian>::split_stack_adjust_reltype(
       std::string from;
       std::string to;
       parameters->target().calls_non_split(this, shndx, p->first, p->second,
+					   prelocs, reloc_count,
 					   view, view_size, &from, &to);
       if (!from.empty())
 	{
diff --git a/gold/target.cc b/gold/target.cc
index f558c5e..19d001c 100644
--- a/gold/target.cc
+++ b/gold/target.cc
@@ -161,7 +161,8 @@ Target::do_is_call_to_non_split(const Symbol* sym, unsigned int) const
 
 void
 Target::do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
-			   section_size_type, unsigned char*, section_size_type,
+			   section_size_type, const unsigned char*, size_t,
+			   unsigned char*, section_size_type,
 			   std::string*, std::string*) const
 {
   static bool warned;
diff --git a/gold/target.h b/gold/target.h
index 6e08f38..d28491c 100644
--- a/gold/target.h
+++ b/gold/target.h
@@ -338,10 +338,12 @@ class Target
   void
   calls_non_split(Relobj* object, unsigned int shndx,
 		  section_offset_type fnoffset, section_size_type fnsize,
+		  const unsigned char* prelocs, size_t reloc_count,
 		  unsigned char* view, section_size_type view_size,
 		  std::string* from, std::string* to) const
   {
-    this->do_calls_non_split(object, shndx, fnoffset, fnsize, view, view_size,
+    this->do_calls_non_split(object, shndx, fnoffset, fnsize,
+			     prelocs, reloc_count, view, view_size,
 			     from, to);
   }
 
@@ -664,7 +666,8 @@ class Target
   // Virtual function which may be overridden by the child class.
   virtual void
   do_calls_non_split(Relobj* object, unsigned int, section_offset_type,
-		     section_size_type, unsigned char*, section_size_type,
+		     section_size_type, const unsigned char*, size_t,
+		     unsigned char*, section_size_type,
 		     std::string*, std::string*) const;
 
   // make_elf_object hooks.  There are four versions of these for
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index 12c6552..f260e74 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -561,6 +561,7 @@ class Target_x86_64 : public Sized_target<size, false>
   void
   do_calls_non_split(Relobj* object, unsigned int shndx,
 		     section_offset_type fnoffset, section_size_type fnsize,
+		     const unsigned char* prelocs, size_t reloc_count,
 		     unsigned char* view, section_size_type view_size,
 		     std::string* from, std::string* to) const;
 
@@ -4597,6 +4598,8 @@ void
 Target_x86_64<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
 					section_offset_type fnoffset,
 					section_size_type fnsize,
+					const unsigned char*,
+					size_t,
 					unsigned char* view,
 					section_size_type view_size,
 					std::string* from,

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-11 22:27     ` Cary Coutant
@ 2015-12-13  2:09       ` Marcin Kościelnicki
  2015-12-13 22:42         ` Cary Coutant
  0 siblings, 1 reply; 11+ messages in thread
From: Marcin Kościelnicki @ 2015-12-13  2:09 UTC (permalink / raw)
  To: Cary Coutant; +Cc: Binutils

[-- Attachment #1: Type: text/plain, Size: 2083 bytes --]

On 11/12/15 23:27, Cary Coutant wrote:
>>> I think it's reasonable to expect that a target implementation of
>>> do_calls_non_split may need prelocs, reloc_count, and Output_file*, so
>>> I don't have a problem adding those to the signatures of
>>> Sized_relobj_file::split_stack_adjust,
>>> Sized_relobj_file::split_stack_adjust_reltype,
>>> Target::calls_non_split, and Target::do_calls_non_split.
>>
>> With this patch, we won't have to add the Output_file pointer to these
>> function signatures.
>
> I've also committed the attached patch to add prelocs and reloc_count
> parameters to Target::calls_non_split and Target::do_calls_non_split.
>
> -cary

Thanks for the patches!  I've updated mine, and the changes are now 
limitted to s390-specific code.  The output_view patch has a minor 
problem: it returns a const unsigned char *, but I need a read-write 
view (I have to bump the frame size in the parameter block).  I've just 
const_casted around it for now, but I suppose we should change the 
return type instead?

Also, I still have an icky static_cast in the "find parameter block" 
sequence. but I suppose it's here to stay:

+             Sized_relobj_file<size, true> *object_sized =
+               static_cast<Sized_relobj_file<size, true> *>(object);
+             const Symbol_value<size>* sym = 
object_sized->local_symbol(r_sym);
+             bool param_shndx_ordinary;
+             const unsigned int param_shndx =
+               sym->input_shndx(&param_shndx_ordinary);
+             if (!param_shndx_ordinary)
+               goto bad;
+             param_offset = sym->input_value() + reloc.get_r_addend() - 2
+                            - 
object->output_section(param_shndx)->address();
+             param_view = const_cast<unsigned char*>(
+                             object->get_output_view(param_shndx,
+                                                     &param_view_size));

I have also fixed the line lengths and added the ChangeLog.

Btw, this patch shouldn't be landing yet, I still have to finish the gcc 
side.

Marcin Kościelnicki

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-gold-Add-s390-fsplit-stack-support.patch --]
[-- Type: text/x-patch; name="0001-gold-Add-s390-fsplit-stack-support.patch", Size: 55870 bytes --]

From 451e886bf9c95312d0aaeb35d3f7d9ae2062482b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20Ko=C5=9Bcielnicki?= <koriakin@0x04.net>
Date: Sun, 13 Dec 2015 02:53:09 +0100
Subject: [PATCH] gold: Add s390 -fsplit-stack support.

gold/ChangeLog:

	* s390.cc (Target_s390::match_view_u): New helper method.
	(Target_s390::ss_code_nopmark): New const.
	(Target_s390::ss_code_ear): New const.
	(Target_s390::ss_code_c): New const.
	(Target_s390::ss_code_l): New const.
	(Target_s390::ss_code_ahi): New const.
	(Target_s390::ss_code_alfi): New const.
	(Target_s390::ss_code_cr): New const.
	(Target_s390::ss_code_larl): New const.
	(Target_s390::ss_code_jg): New const.
	(Target_s390::ss_code_jgl): New const.
	(Target_s390::ss_code_jl): New const.
	(Target_s390::ss_code_jhe): New const.
	(Target_s390::ss_code_basr): New const.
	(Target_s390::ss_code_basr_2): New const.
	(Target_s390::do_calls_non_split): New method.
	* testsuite/Makefile.am: Added new tests.
	* testsuite/split_s390.sh: New test.
	* testsuite/split_s390_1_a1.s: New test.
	* testsuite/split_s390_1_a2.s: New test.
	* testsuite/split_s390_1_e1.s: New test.
	* testsuite/split_s390_1_e2.s: New test.
	* testsuite/split_s390_1_e3.s: New test.
	* testsuite/split_s390_1_e4.s: New test.
	* testsuite/split_s390_1_n.s: New test.
	* testsuite/split_s390_1_z1.s: New test.
	* testsuite/split_s390_1_z2.s: New test.
	* testsuite/split_s390_1_z3.s: New test.
	* testsuite/split_s390_1_z4.s: New test.
	* testsuite/split_s390_2_ns.s: New test.
	* testsuite/split_s390_2_s.s: New test.
	* testsuite/split_s390x_1_a1.s: New test.
	* testsuite/split_s390x_1_a2.s: New test.
	* testsuite/split_s390x_1_n.s: New test.
	* testsuite/split_s390x_1_z1.s: New test.
	* testsuite/split_s390x_1_z2.s: New test.
	* testsuite/split_s390x_1_z3.s: New test.
	* testsuite/split_s390x_1_z4.s: New test.
	* testsuite/split_s390x_2_ns.s: New test.
	* testsuite/split_s390x_2_s.s: New test.
---
 gold/ChangeLog                    |  43 ++++
 gold/s390.cc                      | 453 ++++++++++++++++++++++++++++++++++++++
 gold/testsuite/Makefile.am        | 204 +++++++++++++++++
 gold/testsuite/split_s390.sh      | 149 +++++++++++++
 gold/testsuite/split_s390_1_a1.s  |  36 +++
 gold/testsuite/split_s390_1_a2.s  |  37 ++++
 gold/testsuite/split_s390_1_e1.s  |  45 ++++
 gold/testsuite/split_s390_1_e2.s  |  48 ++++
 gold/testsuite/split_s390_1_e3.s  |  49 +++++
 gold/testsuite/split_s390_1_e4.s  |  51 +++++
 gold/testsuite/split_s390_1_n.s   |  18 ++
 gold/testsuite/split_s390_1_z1.s  |  37 ++++
 gold/testsuite/split_s390_1_z2.s  |  40 ++++
 gold/testsuite/split_s390_1_z3.s  |  41 ++++
 gold/testsuite/split_s390_1_z4.s  |  41 ++++
 gold/testsuite/split_s390_2_ns.s  |  12 +
 gold/testsuite/split_s390_2_s.s   |  13 ++
 gold/testsuite/split_s390x_1_a1.s |  27 +++
 gold/testsuite/split_s390x_1_a2.s |  28 +++
 gold/testsuite/split_s390x_1_n.s  |  17 ++
 gold/testsuite/split_s390x_1_z1.s |  37 ++++
 gold/testsuite/split_s390x_1_z2.s |  41 ++++
 gold/testsuite/split_s390x_1_z3.s |  43 ++++
 gold/testsuite/split_s390x_1_z4.s |  43 ++++
 gold/testsuite/split_s390x_2_ns.s |  12 +
 gold/testsuite/split_s390x_2_s.s  |  13 ++
 26 files changed, 1578 insertions(+)
 create mode 100755 gold/testsuite/split_s390.sh
 create mode 100644 gold/testsuite/split_s390_1_a1.s
 create mode 100644 gold/testsuite/split_s390_1_a2.s
 create mode 100644 gold/testsuite/split_s390_1_e1.s
 create mode 100644 gold/testsuite/split_s390_1_e2.s
 create mode 100644 gold/testsuite/split_s390_1_e3.s
 create mode 100644 gold/testsuite/split_s390_1_e4.s
 create mode 100644 gold/testsuite/split_s390_1_n.s
 create mode 100644 gold/testsuite/split_s390_1_z1.s
 create mode 100644 gold/testsuite/split_s390_1_z2.s
 create mode 100644 gold/testsuite/split_s390_1_z3.s
 create mode 100644 gold/testsuite/split_s390_1_z4.s
 create mode 100644 gold/testsuite/split_s390_2_ns.s
 create mode 100644 gold/testsuite/split_s390_2_s.s
 create mode 100644 gold/testsuite/split_s390x_1_a1.s
 create mode 100644 gold/testsuite/split_s390x_1_a2.s
 create mode 100644 gold/testsuite/split_s390x_1_n.s
 create mode 100644 gold/testsuite/split_s390x_1_z1.s
 create mode 100644 gold/testsuite/split_s390x_1_z2.s
 create mode 100644 gold/testsuite/split_s390x_1_z3.s
 create mode 100644 gold/testsuite/split_s390x_1_z4.s
 create mode 100644 gold/testsuite/split_s390x_2_ns.s
 create mode 100644 gold/testsuite/split_s390x_2_s.s

diff --git a/gold/ChangeLog b/gold/ChangeLog
index e039ab1..3c8f992 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,46 @@
+2015-12-13  Marcin Kościelnicki  <koriakin@0x04.net>
+
+	* s390.cc (Target_s390::match_view_u): New helper method.
+	(Target_s390::ss_code_nopmark): New const.
+	(Target_s390::ss_code_ear): New const.
+	(Target_s390::ss_code_c): New const.
+	(Target_s390::ss_code_l): New const.
+	(Target_s390::ss_code_ahi): New const.
+	(Target_s390::ss_code_alfi): New const.
+	(Target_s390::ss_code_cr): New const.
+	(Target_s390::ss_code_larl): New const.
+	(Target_s390::ss_code_jg): New const.
+	(Target_s390::ss_code_jgl): New const.
+	(Target_s390::ss_code_jl): New const.
+	(Target_s390::ss_code_jhe): New const.
+	(Target_s390::ss_code_basr): New const.
+	(Target_s390::ss_code_basr_2): New const.
+	(Target_s390::do_calls_non_split): New method.
+	* testsuite/Makefile.am: Added new tests.
+	* testsuite/split_s390.sh: New test.
+	* testsuite/split_s390_1_a1.s: New test.
+	* testsuite/split_s390_1_a2.s: New test.
+	* testsuite/split_s390_1_e1.s: New test.
+	* testsuite/split_s390_1_e2.s: New test.
+	* testsuite/split_s390_1_e3.s: New test.
+	* testsuite/split_s390_1_e4.s: New test.
+	* testsuite/split_s390_1_n.s: New test.
+	* testsuite/split_s390_1_z1.s: New test.
+	* testsuite/split_s390_1_z2.s: New test.
+	* testsuite/split_s390_1_z3.s: New test.
+	* testsuite/split_s390_1_z4.s: New test.
+	* testsuite/split_s390_2_ns.s: New test.
+	* testsuite/split_s390_2_s.s: New test.
+	* testsuite/split_s390x_1_a1.s: New test.
+	* testsuite/split_s390x_1_a2.s: New test.
+	* testsuite/split_s390x_1_n.s: New test.
+	* testsuite/split_s390x_1_z1.s: New test.
+	* testsuite/split_s390x_1_z2.s: New test.
+	* testsuite/split_s390x_1_z3.s: New test.
+	* testsuite/split_s390x_1_z4.s: New test.
+	* testsuite/split_s390x_2_ns.s: New test.
+	* testsuite/split_s390x_2_s.s: New test.
+
 2015-12-11  Cary Coutant  <ccoutant@gmail.com>
 
 	* target.h (Target::calls_non_split): Add prelocs, reloc_count
diff --git a/gold/s390.cc b/gold/s390.cc
index 45c0ba7..91cb880 100644
--- a/gold/s390.cc
+++ b/gold/s390.cc
@@ -402,6 +402,14 @@ class Target_s390 : public Sized_target<size, true>
   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,
+		     const unsigned char* prelocs, size_t reloc_count,
+		     unsigned char* view, section_size_type view_size,
+		     std::string* from, std::string* to) const;
+
   // Return the size of the GOT section.
   section_size_type
   got_size() const
@@ -687,6 +695,17 @@ class Target_s390 : public Sized_target<size, true>
 				  this->rela_dyn_section(layout));
   }
 
+  // A function for targets to call.  Return whether BYTES/LEN matches
+  // VIEW/VIEW_SIZE at OFFSET.  Like the one in Target, but takes
+  // an unsigned char * parameter.
+  bool
+  match_view_u(const unsigned char* view, section_size_type view_size,
+     section_offset_type offset, const unsigned char* bytes, size_t len) const
+    {
+      return this->match_view(view, view_size, offset,
+			      reinterpret_cast<const char*>(bytes), len);
+    }
+
   // Information about this specific target which we pass to the
   // general Target structure.
   static Target::Target_info s390_info;
@@ -724,6 +743,22 @@ class Target_s390 : public Sized_target<size, true>
   bool tls_base_symbol_defined_;
   // For use in do_tls_offset_for_*
   Layout *layout_;
+
+  // Code sequences for -fsplit-stack matching.
+  static const unsigned char ss_code_nopmark[];
+  static const unsigned char ss_code_ear[];
+  static const unsigned char ss_code_c[];
+  static const unsigned char ss_code_l[];
+  static const unsigned char ss_code_ahi[];
+  static const unsigned char ss_code_alfi[];
+  static const unsigned char ss_code_cr[];
+  static const unsigned char ss_code_larl[];
+  static const unsigned char ss_code_jg[];
+  static const unsigned char ss_code_jgl[];
+  static const unsigned char ss_code_jl[];
+  static const unsigned char ss_code_jhe[];
+  static const unsigned char ss_code_basr[];
+  static const unsigned char ss_code_basr_2[];
 };
 
 template<>
@@ -4243,6 +4278,424 @@ Target_s390<size>::do_code_fill(section_size_type length) const
   return std::string(length, static_cast<char>(0x07));
 }
 
+// Code sequences to match below.
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_nopmark[] = {
+  0x07, 0x0f,				// nopr %r15
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_ear[] = {
+  0xb2, 0x4f, 0x00, 0x10,		// ear %r1, %a0
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_ear[] = {
+  0xb2, 0x4f, 0x00, 0x10,		// ear %r1, %a0
+  0xeb, 0x11, 0x00, 0x20, 0x00, 0x0d,	// sllg %r1,%r1,32
+  0xb2, 0x4f, 0x00, 0x11,		// ear %r1, %a1
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_c[] = {
+  0x59, 0xf0, 0x10, 0x20,		// c %r15, 0x20(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_c[] = {
+  0xe3, 0xf0, 0x10, 0x38, 0x00, 0x20,	// cg %r15, 0x38(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_l[] = {
+  0x58, 0x10, 0x10, 0x20,		// l %r1, 0x20(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_l[] = {
+  0xe3, 0x10, 0x10, 0x38, 0x00, 0x04,	// lg %r1, 0x38(%r1)
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_ahi[] = {
+  0xa7, 0x1a,				// ahi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_ahi[] = {
+  0xa7, 0x1b,				// aghi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_alfi[] = {
+  0xc2, 0x1b,				// alfi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_alfi[] = {
+  0xc2, 0x1a,				// algfi %r1, ...
+};
+
+template<>
+const unsigned char
+Target_s390<32>::ss_code_cr[] = {
+  0x19, 0xf1,				// cr %r15, %r1
+};
+
+template<>
+const unsigned char
+Target_s390<64>::ss_code_cr[] = {
+  0xb9, 0x20, 0x00, 0xf1,		// cgr %r15, %r1
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_larl[] = {
+  0xc0, 0x10		,		// larl ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jg[] = {
+  0xc0, 0xf4		,		// jg ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jgl[] = {
+  0xc0, 0x44,				// jgl ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jl[] = {
+  0xa7, 0x44,				// jl ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_jhe[] = {
+  0xa7, 0xa4,				// jhe ...
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_basr[] = {
+  0x0d, 0x10,				// basr %r1, 0
+  0x5a, 0x10, 0x10, 0x12,		// a %r1, 0x12(%r1)
+  0x0d, 0x11,				// basr %r1, %r1
+};
+
+template<int size>
+const unsigned char
+Target_s390<size>::ss_code_basr_2[] = {
+  0x0d, 0x10,				// basr %r1, 0
+  0x5a, 0x10, 0x10, 0x14,		// a %r1, 0x14(%r1)
+  0x0d, 0x11,				// basr %r1, %r1
+};
+
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fsplit-stack.  The function calls non-split-stack
+// code.  We have to change the function so that it always ensures
+// that it has enough stack space to run some random function.
+
+template<int size>
+void
+Target_s390<size>::do_calls_non_split(Relobj* object, unsigned int shndx,
+				      section_offset_type fnoffset,
+				      section_size_type,
+				      const unsigned char *prelocs,
+				      size_t reloc_count,
+				      unsigned char* view,
+				      section_size_type view_size,
+				      std::string*,
+				      std::string*) const
+{
+  // true if there's a conditional call to __morestack in the function,
+  // false if there's an unconditional one.
+  bool conditional = false;
+  // Offset of the byte after the compare insn, if conditional.
+  section_offset_type cmpend = 0;
+  // Type and immediate offset of the add instruction that adds frame size
+  // to guard.
+  enum {
+    SS_ADD_NONE,
+    SS_ADD_AHI,
+    SS_ADD_ALFI,
+  } fsadd_type = SS_ADD_NONE;
+  section_offset_type fsadd_offset = 0;
+  uint32_t fsadd_frame_size = 0;
+  // Type and offset of the conditional jump.
+  enum {
+    SS_JUMP_NONE,
+    SS_JUMP_JL,
+    SS_JUMP_JHE,
+  } jump_type = SS_JUMP_NONE;
+  section_offset_type jump_offset = 0;
+  // Section view and offset of param block.
+  section_offset_type param_offset = 0;
+  unsigned char *param_view = 0;
+  section_size_type param_view_size = 0;
+  // Current position in function.
+  section_offset_type curoffset = fnoffset;
+  // Frame size.
+  typename elfcpp::Elf_types<size>::Elf_Addr frame_size;
+  // Relocation parsing.
+  typedef typename Reloc_types<elfcpp::SHT_RELA, size, true>::Reloc Reltype;
+  const int reloc_size = Reloc_types<elfcpp::SHT_RELA, size, true>::reloc_size;
+
+  // If the function starts with a nopr %r15, it's a split-stack-compiled
+  // function that doesn't have a stack frame.  Ignore it.
+
+  if (this->match_view_u(view, view_size, curoffset, ss_code_nopmark,
+		       sizeof ss_code_nopmark))
+    return;
+
+  // First, figure out if there's a conditional call by looking for the
+  // extract-tp, add, cmp sequence.
+
+  if (this->match_view_u(view, view_size, curoffset, ss_code_ear,
+		       sizeof ss_code_ear))
+    {
+      // Found extract-tp, now look for an add and compare.
+      curoffset += sizeof ss_code_ear;
+      conditional = true;
+      if (this->match_view_u(view, view_size, curoffset, ss_code_c,
+			   sizeof ss_code_c))
+	{
+	  // Found a direct compare of stack pointer with the guard,
+	  // we're done here.
+	  curoffset += sizeof ss_code_c;
+	}
+      else if (this->match_view_u(view, view_size, curoffset, ss_code_l,
+			   sizeof ss_code_l))
+	{
+	  // Found a load of guard to r1, look for an add and compare.
+	  curoffset += sizeof ss_code_l;
+          if (this->match_view_u(view, view_size, curoffset, ss_code_ahi,
+			       sizeof ss_code_ahi))
+	    {
+	      curoffset += sizeof ss_code_ahi;
+	      // Found an ahi, extract its immediate
+	      fsadd_offset = curoffset;
+	      fsadd_type = SS_ADD_AHI;
+	      if (convert_to_section_size_type(curoffset + 2) > view_size)
+	        goto bad;
+	      fsadd_frame_size
+	        = elfcpp::Swap<16, true>::readval(view + curoffset);
+	      curoffset += 2;
+	    }
+	  else if (this->match_view_u(view, view_size, curoffset,
+		    ss_code_alfi, sizeof ss_code_alfi))
+	    {
+	      curoffset += sizeof ss_code_alfi;
+	      // Found an alfi, extract its immediate
+	      fsadd_offset = curoffset;
+	      fsadd_type = SS_ADD_ALFI;
+	      if (convert_to_section_size_type(curoffset + 4) > view_size)
+	        goto bad;
+	      fsadd_frame_size
+	        = elfcpp::Swap_unaligned<32, true>::readval(view + curoffset);
+	      curoffset += 4;
+	    }
+	  else
+            {
+	      goto bad;
+            }
+	  // Now, there has to be a compare.
+          if (this->match_view_u(view, view_size, curoffset, ss_code_cr,
+			       sizeof ss_code_cr))
+	    curoffset += sizeof ss_code_cr;
+	  else
+	    goto bad;
+	}
+      else
+        {
+	  goto bad;
+        }
+      cmpend = curoffset;
+    }
+
+  // Second, look for the call.
+
+  if (this->match_view_u(view, view_size, curoffset, ss_code_larl,
+		       sizeof ss_code_larl))
+    {
+      // Found a zarch-style call: larl + jg
+      curoffset += sizeof ss_code_larl;
+
+      // Find out larl's operand.  It should be a local symbol in .rodata
+      // section.
+      const unsigned char *pr = prelocs;
+      for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size)
+        {
+          Reltype reloc(pr);
+	  if (static_cast<section_offset_type>(reloc.get_r_offset())
+	      == curoffset)
+	    {
+              typename elfcpp::Elf_types<size>::Elf_WXword r_info
+	        = reloc.get_r_info();
+              unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+              unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+	      if (r_type != elfcpp::R_390_PC32DBL)
+	        goto bad;
+	      if (r_sym >= object->local_symbol_count())
+		goto bad;
+	      Sized_relobj_file<size, true> *object_sized =
+		static_cast<Sized_relobj_file<size, true> *>(object);
+              const Symbol_value<size>* sym = object_sized->local_symbol(r_sym);
+	      bool param_shndx_ordinary;
+	      const unsigned int param_shndx =
+		sym->input_shndx(&param_shndx_ordinary);
+	      if (!param_shndx_ordinary)
+		goto bad;
+	      param_offset = sym->input_value() + reloc.get_r_addend() - 2
+			     - object->output_section(param_shndx)->address();
+	      param_view = const_cast<unsigned char*>(
+			      object->get_output_view(param_shndx,
+						      &param_view_size));
+	      break;
+	    }
+	}
+
+      if (!param_view)
+	goto bad;
+
+      curoffset += 4;
+
+      // Now, there has to be a jump to __morestack.
+      jump_offset = curoffset;
+
+      if (this->match_view_u(view, view_size, curoffset,
+			   conditional ? ss_code_jgl : ss_code_jg,
+			   sizeof ss_code_jg))
+	curoffset += sizeof ss_code_jg;
+      else
+	goto bad;
+
+      if (conditional)
+        jump_type = SS_JUMP_JL;
+
+      curoffset += 4;
+    }
+  else if (size == 32)
+    {
+      // There'll be an ESA-style call, possibly with a cond jump first.
+      if (conditional)
+	{
+	  if (this->match_view_u(view, view_size, curoffset, ss_code_jl,
+			       sizeof ss_code_jl))
+	    {
+	      // Jump if morestack call needed - we need to follow it.
+	      int16_t off;
+	      jump_type = SS_JUMP_JL;
+	      jump_offset = curoffset;
+	      curoffset += sizeof ss_code_jl;
+	      if (convert_to_section_size_type(curoffset + 2) > view_size)
+	        goto bad;
+	      off = elfcpp::Swap<16, true>::readval(view + curoffset);
+	      curoffset += off * 2 - sizeof ss_code_jl;
+	    }
+	  else if (this->match_view_u(view, view_size, curoffset, ss_code_jhe,
+				    sizeof ss_code_jhe))
+	    {
+	      jump_type = SS_JUMP_JHE;
+	      // Jump if morestack not needed - skip over it.  Bump cmpend,
+	      // so that jump is nopped out along with the rest if we need
+	      // to make the call unconditional.
+	      curoffset += sizeof ss_code_jhe;
+	      curoffset += 2;
+	      cmpend = curoffset;
+	    }
+	  else
+	    {
+	      goto bad;
+	    }
+	}
+
+      // Match the basr sequence. There are two variants, depending on
+      // whether the start is 4-aligned or not.
+      if (this->match_view_u(view, view_size, curoffset, ss_code_basr,
+			   sizeof ss_code_basr))
+	curoffset += sizeof ss_code_basr;
+      else if (this->match_view_u(view, view_size, curoffset, ss_code_basr_2,
+			   sizeof ss_code_basr_2))
+	curoffset += sizeof ss_code_basr_2 + 2;
+      else
+        goto bad;
+
+      // We found the param block.
+      param_offset = curoffset;
+      param_view = view;
+      param_view_size = view_size;
+    }
+  else
+    {
+      goto bad;
+    }
+
+  // Read the frame size.
+  if (convert_to_section_size_type(param_offset + size / 8) > param_view_size)
+    goto bad;
+  frame_size = elfcpp::Swap<size, true>::readval(param_view + param_offset);
+
+  // Sanity check.
+  if (fsadd_type != SS_ADD_NONE && fsadd_frame_size != frame_size)
+    goto bad;
+
+  // Bump the frame size.
+  frame_size += parameters->options().split_stack_adjust_size();
+
+  // Store it to the param block.
+  elfcpp::Swap<size, true>::writeval(param_view + param_offset, frame_size);
+
+  if (!conditional)
+    {
+      // If the call was already unconditional, we're done.
+    }
+  else if (frame_size <= 0xffffffff && fsadd_type == SS_ADD_ALFI)
+    {
+      // Using alfi to add the frame size, and it still fits.  Adjust it.
+      elfcpp::Swap_unaligned<32, true>::writeval(view + fsadd_offset,
+						 frame_size);
+    }
+  else
+    {
+      // We were either relying on the backoff area, or used ahi to load
+      // frame size.  This won't fly, as our new frame size is too large.
+      // Convert the sequence to unconditional by nopping out the comparison,
+      // and rewiring the jump.
+      this->set_view_to_nop(view, view_size, fnoffset, cmpend - fnoffset);
+
+      // If jump type was jhe, we already nopped it out above.
+      if (jump_type == SS_JUMP_JL)
+        {
+	  // If jump is jl/jgl, we'll mutate it to j/jg.
+	  view[jump_offset+1] = 0xf4;
+	}
+    }
+
+  return;
+
+bad:
+  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));
+}
+
 // Relocate section data.
 
 template<int size>
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index c8e5f9f..7cc7a2d 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -3362,6 +3362,210 @@ MOSTLYCLEANFILES += arm_farcall_thumb_arm arm_farcall_thumb_arm_5t
 
 endif DEFAULT_TARGET_ARM
 
+if DEFAULT_TARGET_S390
+
+check_SCRIPTS += split_s390.sh
+check_DATA += split_s390_e1.stdout split_s390_e2.stdout \
+	split_s390_e3.stdout split_s390_e4.stdout split_s390_z1.stdout \
+	split_s390_z2.stdout split_s390_z3.stdout split_s390_z4.stdout \
+	split_s390_n.stdout split_s390_a1.stdout split_s390_a2.stdout \
+	split_s390_e1_ns.stdout split_s390_e2_ns.stdout \
+	split_s390_e3_ns.stdout split_s390_e4_ns.stdout \
+	split_s390_z1_ns.stdout split_s390_z2_ns.stdout \
+	split_s390_z3_ns.stdout split_s390_z4_ns.stdout \
+	split_s390_n_ns.stdout split_s390_r.stdout \
+	split_s390x_z1.stdout split_s390x_z2.stdout split_s390x_z3.stdout \
+	split_s390x_z4.stdout split_s390x_n.stdout split_s390x_a1.stdout \
+	split_s390x_a2.stdout split_s390x_z1_ns.stdout \
+	split_s390x_z2_ns.stdout split_s390x_z3_ns.stdout \
+	split_s390x_z4_ns.stdout split_s390x_n_ns.stdout \
+	split_s390x_r.stdout
+SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
+split_s390_1_e1.o: split_s390_1_e1.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_e2.o: split_s390_1_e2.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_e3.o: split_s390_1_e3.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_e4.o: split_s390_1_e4.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z1.o: split_s390_1_z1.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z2.o: split_s390_1_z2.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z3.o: split_s390_1_z3.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_z4.o: split_s390_1_z4.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_n.o: split_s390_1_n.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_a1.o: split_s390_1_a1.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_1_a2.o: split_s390_1_a2.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_2_s.o: split_s390_2_s.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_2_ns.o: split_s390_2_ns.s
+	$(TEST_AS) -m31 -o $@ $<
+split_s390_e1: split_s390_1_e1.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e1.o split_s390_2_s.o
+split_s390_e1.stdout: split_s390_e1
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e2: split_s390_1_e2.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e2.o split_s390_2_s.o
+split_s390_e2.stdout: split_s390_e2
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e3: split_s390_1_e3.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e3.o split_s390_2_s.o
+split_s390_e3.stdout: split_s390_e3
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e4: split_s390_1_e4.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e4.o split_s390_2_s.o
+split_s390_e4.stdout: split_s390_e4
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_z1: split_s390_1_z1.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z1.o split_s390_2_s.o
+split_s390_z1.stdout: split_s390_z1
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z2: split_s390_1_z2.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z2.o split_s390_2_s.o
+split_s390_z2.stdout: split_s390_z2
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z3: split_s390_1_z3.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z3.o split_s390_2_s.o
+split_s390_z3.stdout: split_s390_z3
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z4: split_s390_1_z4.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z4.o split_s390_2_s.o
+split_s390_z4.stdout: split_s390_z4
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_n: split_s390_1_n.o split_s390_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_n.o split_s390_2_s.o
+split_s390_n.stdout: split_s390_n
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e1_ns: split_s390_1_e1.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e1.o split_s390_2_ns.o
+split_s390_e1_ns.stdout: split_s390_e1_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e2_ns: split_s390_1_e2.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e2.o split_s390_2_ns.o
+split_s390_e2_ns.stdout: split_s390_e2_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e3_ns: split_s390_1_e3.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e3.o split_s390_2_ns.o
+split_s390_e3_ns.stdout: split_s390_e3_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_e4_ns: split_s390_1_e4.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_e4.o split_s390_2_ns.o
+split_s390_e4_ns.stdout: split_s390_e4_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_z1_ns: split_s390_1_z1.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z1.o split_s390_2_ns.o
+split_s390_z1_ns.stdout: split_s390_z1_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z2_ns: split_s390_1_z2.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z2.o split_s390_2_ns.o
+split_s390_z2_ns.stdout: split_s390_z2_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z3_ns: split_s390_1_z3.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z3.o split_s390_2_ns.o
+split_s390_z3_ns.stdout: split_s390_z3_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_z4_ns: split_s390_1_z4.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_z4.o split_s390_2_ns.o
+split_s390_z4_ns.stdout: split_s390_z4_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390_n_ns: split_s390_1_n.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_n.o split_s390_2_ns.o
+split_s390_n_ns.stdout: split_s390_n_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_a1.stdout: split_s390_1_a1.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o split_s390_a1 split_s390_1_a1.o split_s390_2_ns.o > $@ 2>&1 || exit 0
+split_s390_a2: split_s390_1_a2.o split_s390_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390_1_a2.o split_s390_2_ns.o
+split_s390_a2.stdout: split_s390_a2
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390_r.stdout: split_s390_1_z1.o split_s390_2_ns.o ../ld-new
+	../ld-new -r split_s390_1_z1.o split_s390_2_ns.o -o split_s390_r > $@ 2>&1 || exit 0
+split_s390x_1_z1.o: split_s390x_1_z1.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_z2.o: split_s390x_1_z2.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_z3.o: split_s390x_1_z3.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_z4.o: split_s390x_1_z4.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_n.o: split_s390x_1_n.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_a1.o: split_s390x_1_a1.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_1_a2.o: split_s390x_1_a2.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_2_s.o: split_s390x_2_s.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_2_ns.o: split_s390x_2_ns.s
+	$(TEST_AS) -m64 -o $@ $<
+split_s390x_z1: split_s390x_1_z1.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z1.o split_s390x_2_s.o
+split_s390x_z1.stdout: split_s390x_z1
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z2: split_s390x_1_z2.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z2.o split_s390x_2_s.o
+split_s390x_z2.stdout: split_s390x_z2
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z3: split_s390x_1_z3.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z3.o split_s390x_2_s.o
+split_s390x_z3.stdout: split_s390x_z3
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z4: split_s390x_1_z4.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z4.o split_s390x_2_s.o
+split_s390x_z4.stdout: split_s390x_z4
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_n: split_s390x_1_n.o split_s390x_2_s.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_n.o split_s390x_2_s.o
+split_s390x_n.stdout: split_s390x_n
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390x_z1_ns: split_s390x_1_z1.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z1.o split_s390x_2_ns.o
+split_s390x_z1_ns.stdout: split_s390x_z1_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z2_ns: split_s390x_1_z2.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z2.o split_s390x_2_ns.o
+split_s390x_z2_ns.stdout: split_s390x_z2_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z3_ns: split_s390x_1_z3.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z3.o split_s390x_2_ns.o
+split_s390x_z3_ns.stdout: split_s390x_z3_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_z4_ns: split_s390x_1_z4.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_z4.o split_s390x_2_ns.o
+split_s390x_z4_ns.stdout: split_s390x_z4_ns
+	$(TEST_OBJDUMP) -j .rodata -j .text -D $< > $@
+split_s390x_n_ns: split_s390x_1_n.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_n.o split_s390x_2_ns.o
+split_s390x_n_ns.stdout: split_s390x_n_ns
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390x_a1.stdout: split_s390x_1_a1.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o split_s390x_a1 split_s390x_1_a1.o split_s390x_2_ns.o > $@ 2>&1 || exit 0
+split_s390x_a2: split_s390x_1_a2.o split_s390x_2_ns.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_s390x_1_a2.o split_s390x_2_ns.o
+split_s390x_a2.stdout: split_s390x_a2
+	$(TEST_OBJDUMP) -d $< > $@
+split_s390x_r.stdout: split_s390x_1_z1.o split_s390x_2_ns.o ../ld-new
+	../ld-new -r split_s390x_1_z1.o split_s390x_2_ns.o -o split_s390x_r > $@ 2>&1 || exit 0
+MOSTLYCLEANFILES += split_s390_1_e1 split_s390_1_e2 split_s390_1_e3 \
+	split_s390_1_e4 split_s390_1_z1 split_s390_1_z2 split_s390_1_z3 \
+	split_s390_1_z4 split_s390_1_n split_s390_1_a1 split_s390_1_a2 \
+	split_s390_1_e1_ns split_s390_1_e2_ns split_s390_1_e3_ns \
+	split_s390_1_e4_ns split_s390_1_z1_ns split_s390_1_z2_ns \
+	split_s390_1_z3_ns split_s390_1_z4_ns split_s390_1_n_ns \
+	split_s390_r split_s390x_1_z1 split_s390x_1_z2 split_s390x_1_z3 \
+	split_s390x_1_z4 split_s390x_1_n split_s390x_1_a1 split_s390x_1_a2 \
+	split_s390x_1_z1_ns split_s390x_1_z2_ns split_s390x_1_z3_ns \
+	split_s390x_1_z4_ns split_s390x_1_n_ns split_s390x_r
+
+endif DEFAULT_TARGET_S390
+
 endif NATIVE_OR_CROSS_LINKER
 
 # Tests for the dwp tool.
diff --git a/gold/testsuite/split_s390.sh b/gold/testsuite/split_s390.sh
new file mode 100755
index 0000000..96add07
--- /dev/null
+++ b/gold/testsuite/split_s390.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+# split_s390.sh -- test -fstack-split for s390
+
+# Copyright (C) 2009-2015 Free Software Foundation, Inc.
+# Written by Marcin Kościelnicki <koriakin@0x04.net>.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+match()
+{
+  if ! egrep "$1" "$2" >/dev/null 2>&1; then
+    echo 1>&2 "could not find '$1' in $2"
+    exit 1
+  fi
+}
+
+nomatch()
+{
+  if egrep "$1" "$2" >/dev/null 2>&1; then
+    echo 1>&2 "found unexpected '$1' in $2"
+    exit 1
+  fi
+}
+
+match 'basr.*%r1,%r0$' split_s390_e1.stdout
+match 'long.*0x00100000$' split_s390_e1.stdout
+match 'basr.*%r1,%r0$' split_s390_e1_ns.stdout
+match 'long.*0x00104000$' split_s390_e1_ns.stdout
+
+match 'ear.*$' split_s390_e2.stdout
+match 'jhe.*$' split_s390_e2.stdout
+match 'basr.*%r1,%r0$' split_s390_e2.stdout
+match 'long.*0x00000100$' split_s390_e2.stdout
+nomatch 'ear.*$' split_s390_e2_ns.stdout
+nomatch 'jhe.*$' split_s390_e2_ns.stdout
+match 'basr.*%r1,%r0$' split_s390_e2_ns.stdout
+match 'long.*0x00004100$' split_s390_e2_ns.stdout
+
+match 'ear.*$' split_s390_e3.stdout
+match 'jl.*$' split_s390_e3.stdout
+nomatch 'j	.*$' split_s390_e3.stdout
+match 'basr.*%r1,%r0$' split_s390_e3.stdout
+match 'long.*0x00000100$' split_s390_e3.stdout
+nomatch 'ear.*$' split_s390_e3_ns.stdout
+nomatch 'jl.*$' split_s390_e3_ns.stdout
+match 'j	.*$' split_s390_e3_ns.stdout
+match 'basr.*%r1,%r0$' split_s390_e3_ns.stdout
+match 'long.*0x00004100$' split_s390_e3_ns.stdout
+
+match 'ear.*$' split_s390_e4.stdout
+match 'jl.*$' split_s390_e4.stdout
+nomatch 'j	.*$' split_s390_e4.stdout
+match 'basr.*%r1,%r0$' split_s390_e4.stdout
+match 'long.*0x00001000$' split_s390_e4.stdout
+nomatch 'ear.*$' split_s390_e4_ns.stdout
+nomatch 'jl.*$' split_s390_e4_ns.stdout
+match 'j	.*$' split_s390_e4_ns.stdout
+match 'basr.*%r1,%r0$' split_s390_e4_ns.stdout
+match 'long.*0x00005000$' split_s390_e4_ns.stdout
+
+match 'jg.*__morestack>?$' split_s390_z1.stdout
+match 'long.*0x00100000$' split_s390_z1.stdout
+match 'jg.*__morestack>?$' split_s390_z1_ns.stdout
+match 'long.*0x00104000$' split_s390_z1_ns.stdout
+
+match 'ear.*$' split_s390_z2.stdout
+match 'jgl.*__morestack>?$' split_s390_z2.stdout
+nomatch 'jg	.*__morestack>?$' split_s390_z2.stdout
+match 'long.*0x00000100$' split_s390_z2.stdout
+nomatch 'ear.*$' split_s390_z2_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390_z2_ns.stdout
+match 'jg	.*__morestack>?$' split_s390_z2_ns.stdout
+match 'long.*0x00004100$' split_s390_z2_ns.stdout
+
+match 'ear.*$' split_s390_z3.stdout
+match 'jgl.*__morestack>?$' split_s390_z3.stdout
+nomatch 'jg	.*__morestack>?$' split_s390_z3.stdout
+match 'long.*0x00001000$' split_s390_z3.stdout
+nomatch 'ear.*$' split_s390_z3_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390_z3_ns.stdout
+match 'jg	.*__morestack>?$' split_s390_z3_ns.stdout
+match 'long.*0x00005000$' split_s390_z3_ns.stdout
+
+match 'alfi.*%r1,1048576$' split_s390_z4.stdout
+match 'jgl.*__morestack>?$' split_s390_z4.stdout
+match 'long.*0x00100000$' split_s390_z4.stdout
+match 'alfi.*%r1,1064960$' split_s390_z4_ns.stdout
+match 'jgl.*__morestack>?$' split_s390_z4_ns.stdout
+match 'long.*0x00104000$' split_s390_z4_ns.stdout
+
+match 'jg.*__morestack>?$' split_s390x_z1.stdout
+match 'long.*0x00100000$' split_s390x_z1.stdout
+match 'jg.*__morestack>?$' split_s390x_z1_ns.stdout
+match 'long.*0x00104000$' split_s390x_z1_ns.stdout
+
+match 'ear.*$' split_s390x_z2.stdout
+match 'jgl.*__morestack>?$' split_s390x_z2.stdout
+nomatch 'jg	.*__morestack>?$' split_s390x_z2.stdout
+match 'long.*0x00000100$' split_s390x_z2.stdout
+nomatch 'ear.*$' split_s390x_z2_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390x_z2_ns.stdout
+match 'jg	.*__morestack>?$' split_s390x_z2_ns.stdout
+match 'long.*0x00004100$' split_s390x_z2_ns.stdout
+
+match 'ear.*$' split_s390x_z3.stdout
+match 'jgl.*__morestack>?$' split_s390x_z3.stdout
+nomatch 'jg	.*__morestack>?$' split_s390x_z3.stdout
+match 'long.*0x00001000$' split_s390x_z3.stdout
+nomatch 'ear.*$' split_s390x_z3_ns.stdout
+nomatch 'jgl.*__morestack>?$' split_s390x_z3_ns.stdout
+match 'jg	.*__morestack>?$' split_s390x_z3_ns.stdout
+match 'long.*0x00005000$' split_s390x_z3_ns.stdout
+
+match 'algfi.*%r1,1048576$' split_s390x_z4.stdout
+match 'jgl.*__morestack>?$' split_s390x_z4.stdout
+match 'long.*0x00100000$' split_s390x_z4.stdout
+match 'algfi.*%r1,1064960$' split_s390x_z4_ns.stdout
+match 'jgl.*__morestack>?$' split_s390x_z4_ns.stdout
+match 'long.*0x00104000$' split_s390x_z4_ns.stdout
+
+match 'nopr.*r15$' split_s390_n.stdout
+match 'nopr.*r15$' split_s390_n_ns.stdout
+match 'nopr.*r15$' split_s390x_n.stdout
+match 'nopr.*r15$' split_s390x_n_ns.stdout
+
+match 'failed to match' split_s390_a1.stdout
+match 'failed to match' split_s390x_a1.stdout
+
+match 'bas.*$' split_s390_a2.stdout
+match 'brasl.*__morestack>?$' split_s390x_a2.stdout
+
+match 'cannot mix' split_s390_r.stdout
+match 'cannot mix' split_s390x_r.stdout
diff --git a/gold/testsuite/split_s390_1_a1.s b/gold/testsuite/split_s390_1_a1.s
new file mode 100644
index 0000000..86c1997
--- /dev/null
+++ b/gold/testsuite/split_s390_1_a1.s
@@ -0,0 +1,36 @@
+# split_s390_1_a1.s: s390 specific, adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L1:
+	l	%r1, .L2-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	l	%r1, .L3-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L2:
+	.long	__morestack-.L1
+.L3:
+	.long	fn2-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_a2.s b/gold/testsuite/split_s390_1_a2.s
new file mode 100644
index 0000000..026752f
--- /dev/null
+++ b/gold/testsuite/split_s390_1_a2.s
@@ -0,0 +1,37 @@
+# split_s390_1_a2.s: s390 specific, permitted adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L1:
+	l	%r1, .L2-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	l	%r1, .L3-.L1(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L2:
+	.long	__morestack-.L1
+.L3:
+	.long	fn2-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
+	.section	.note.GNU-no-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e1.s b/gold/testsuite/split_s390_1_e1.s
new file mode 100644
index 0000000..354dc91
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e1.s
@@ -0,0 +1,45 @@
+# split_s390_e1.s: s390 specific test case for -fsplit-stack -
+# esa mode, unconditional call.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x100000
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e2.s b/gold/testsuite/split_s390_1_e2.s
new file mode 100644
index 0000000..af802fa
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e2.s
@@ -0,0 +1,48 @@
+# split_s390_e2.s: s390 specific test case for -fsplit-stack -
+# esa mode, jhe conditional call, no add.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	c	%r15, 0x20(%r1)
+	jhe	.L4
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x100
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e3.s b/gold/testsuite/split_s390_1_e3.s
new file mode 100644
index 0000000..107419b
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e3.s
@@ -0,0 +1,49 @@
+# split_s390_e3.s: s390 specific test case for -fsplit-stack -
+# esa mode, jl conditional call, no add.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	c	%r15, 0x20(%r1)
+	jl	.L7
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+.L7:
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x100
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_e4.s b/gold/testsuite/split_s390_1_e4.s
new file mode 100644
index 0000000..0687682
--- /dev/null
+++ b/gold/testsuite/split_s390_1_e4.s
@@ -0,0 +1,51 @@
+# split_s390_e4.s: s390 specific test case for -fsplit-stack -
+# esa mode, jl conditional call, ahi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	l	%r1, 0x20(%r1)
+	ahi	%r1, 0x1000
+	cr	%r15, %r1
+	jl	.L7
+.L4:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	basr	%r13, %r0
+.L5:
+	l	%r1, .L6-.L5(%r13)
+	bas	%r14, 0(%r13, %r1)
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.align	4
+.L6:
+	.long	fn2-.L5
+.L7:
+	basr	%r1, %r0
+.L1:
+	a	%r1, .L2-.L1(%r1)
+	basr	%r1, %r1
+	.align	4
+.L3:
+	.long	0x1000
+	.long	0
+	.long	.L4-.L3
+.L2:
+	.long	__morestack-.L1
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_n.s b/gold/testsuite/split_s390_1_n.s
new file mode 100644
index 0000000..ca91262
--- /dev/null
+++ b/gold/testsuite/split_s390_1_n.s
@@ -0,0 +1,18 @@
+# split_s390_n.s: s390 specific test case for -fsplit-stack -
+# no stack frame
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	nopr	%r15
+	larl	%r2, fn2
+	br	%r14
+	.cfi_endproc
+
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_z1.s b/gold/testsuite/split_s390_1_z1.s
new file mode 100644
index 0000000..42adf55
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z1.s
@@ -0,0 +1,37 @@
+# split_s390_z3.s: s390 specific test case for -fsplit-stack -
+# zarch mode, unconditional call
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	larl	%r1, .L1
+	jg	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x100000
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_z2.s b/gold/testsuite/split_s390_1_z2.s
new file mode 100644
index 0000000..2717a83
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z2.s
@@ -0,0 +1,40 @@
+# split_s390_z2.s: s390 specific test case for -fsplit-stack -
+# zarch mode, conditional call, no add
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	c	%r15, 0x20(%r1)
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x100
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
+
diff --git a/gold/testsuite/split_s390_1_z3.s b/gold/testsuite/split_s390_1_z3.s
new file mode 100644
index 0000000..8f0cb54
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z3.s
@@ -0,0 +1,41 @@
+# split_s390_z3.s: s390 specific test case for -fsplit-stack -
+# zarch mode, conditional call, ahi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	l	%r1, 0x20(%r1)
+	ahi	%r1, 0x1000
+	cr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x1000
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_1_z4.s b/gold/testsuite/split_s390_1_z4.s
new file mode 100644
index 0000000..b80bf40
--- /dev/null
+++ b/gold/testsuite/split_s390_1_z4.s
@@ -0,0 +1,41 @@
+# split_s390_z4.s: s390 specific test case for -fsplit-stack -
+# zarch mode, conditional call, alfi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	l	%r1, 0x20(%r1)
+	alfi	%r1, 0x100000
+	cr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 4
+.L1:
+	.long	0x100000
+	.long	0
+	.long	.L2-.L1
+	.previous
+.L2:
+	stm	%r13, %r15, 0x34(%r15)
+	.cfi_offset	%r13, -0x2c
+	.cfi_offset	%r14, -0x28
+	.cfi_offset	%r15, -0x24
+	ahi	%r15, -0x60
+	.cfi_adjust_cfa_offset	0x60
+	brasl	%r14, fn2
+	lm	%r13, %r15, 0x94(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0x60
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_2_ns.s b/gold/testsuite/split_s390_2_ns.s
new file mode 100644
index 0000000..fb5a993
--- /dev/null
+++ b/gold/testsuite/split_s390_2_ns.s
@@ -0,0 +1,12 @@
+# split_s390_2_ns.s: s390 specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gold/testsuite/split_s390_2_s.s b/gold/testsuite/split_s390_2_s.s
new file mode 100644
index 0000000..3df8009
--- /dev/null
+++ b/gold/testsuite/split_s390_2_s.s
@@ -0,0 +1,13 @@
+# split_s390_2_s.s: s390 specific, -fsplit-stack calling -fsplit-stack
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_a1.s b/gold/testsuite/split_s390x_1_a1.s
new file mode 100644
index 0000000..ccc62cb
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_a1.s
@@ -0,0 +1,27 @@
+# split_s390x_1_a1.s: s390x specific, adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, __morestack
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_a2.s b/gold/testsuite/split_s390x_1_a2.s
new file mode 100644
index 0000000..a5c067b
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_a2.s
@@ -0,0 +1,28 @@
+# split_s390x_1_a2.s: s390x specific, permitted adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, __morestack
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
+	.section	.note.GNU-no-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_n.s b/gold/testsuite/split_s390x_1_n.s
new file mode 100644
index 0000000..6e434b8
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_n.s
@@ -0,0 +1,17 @@
+# split_s390x_1_n.s: s390x specific test case for -fsplit-stack -
+# no stack frame.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	nopr	%r15
+	larl	%r2, fn2
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z1.s b/gold/testsuite/split_s390x_1_z1.s
new file mode 100644
index 0000000..4af34cd
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z1.s
@@ -0,0 +1,37 @@
+# split_s390x_1_z1.s: s390x specific test case for -fsplit-stack -
+# unconditional call.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	larl	%r1, .L1
+	jg	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x100000
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z2.s b/gold/testsuite/split_s390x_1_z2.s
new file mode 100644
index 0000000..a184f77
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z2.s
@@ -0,0 +1,41 @@
+# split_s390x_1_z2.s: s390x specific test case for -fsplit-stack -
+# conditional call, no add.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	sllg	%r1, %r1, 32
+	ear	%r1, %a1
+	cg	%r15, 0x38(%r1)
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x100
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z3.s b/gold/testsuite/split_s390x_1_z3.s
new file mode 100644
index 0000000..b0a4b0b
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z3.s
@@ -0,0 +1,43 @@
+# split_s390x_1_z3.s: s390x specific test case for -fsplit-stack -
+# conditional call, ahi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	sllg	%r1, %r1, 32
+	ear	%r1, %a1
+	lg	%r1, 0x38(%r1)
+	aghi	%r1, 0x1000
+	cgr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x1000
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_1_z4.s b/gold/testsuite/split_s390x_1_z4.s
new file mode 100644
index 0000000..a3b942e
--- /dev/null
+++ b/gold/testsuite/split_s390x_1_z4.s
@@ -0,0 +1,43 @@
+# split_s390x_1_z4.s: s390x specific test case for -fsplit-stack -
+# conditional call, alfi.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	.cfi_startproc
+	ear	%r1, %a0
+	sllg	%r1, %r1, 32
+	ear	%r1, %a1
+	lg	%r1, 0x38(%r1)
+	algfi	%r1, 0x100000
+	cgr	%r15, %r1
+	larl	%r1, .L1
+	jgl	__morestack
+	.section .rodata
+	.align 8
+.L1:
+	.quad	0x100000
+	.quad	0
+	.quad	.L2-.L1
+	.previous
+.L2:
+	stmg	%r13, %r15, 0x68(%r15)
+	.cfi_offset	%r13, -0x38
+	.cfi_offset	%r14, -0x30
+	.cfi_offset	%r15, -0x28
+	aghi	%r15, -0xa0
+	.cfi_adjust_cfa_offset	0xa0
+	brasl	%r14, fn2
+	lmg	%r13, %r15, 0x108(%r15)
+	.cfi_restore	%r13
+	.cfi_restore	%r14
+	.cfi_restore	%r15
+	.cfi_adjust_cfa_offset	-0xa0
+	br	%r14
+	.cfi_endproc
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_2_ns.s b/gold/testsuite/split_s390x_2_ns.s
new file mode 100644
index 0000000..f073eee
--- /dev/null
+++ b/gold/testsuite/split_s390x_2_ns.s
@@ -0,0 +1,12 @@
+# split_s390x_2_ns.s: s390x specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gold/testsuite/split_s390x_2_s.s b/gold/testsuite/split_s390x_2_s.s
new file mode 100644
index 0000000..787d2fe
--- /dev/null
+++ b/gold/testsuite/split_s390x_2_s.s
@@ -0,0 +1,13 @@
+# split_s390x_2_s.s: s390x specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	br	%r14
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
-- 
2.6.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-13  2:09       ` Marcin Kościelnicki
@ 2015-12-13 22:42         ` Cary Coutant
  2015-12-13 22:43           ` Cary Coutant
  0 siblings, 1 reply; 11+ messages in thread
From: Cary Coutant @ 2015-12-13 22:42 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

> Thanks for the patches!  I've updated mine, and the changes are now limitted
> to s390-specific code.  The output_view patch has a minor problem: it
> returns a const unsigned char *, but I need a read-write view (I have to
> bump the frame size in the parameter block).  I've just const_casted around
> it for now, but I suppose we should change the return type instead?

Oops, sorry! I've committed the attached patch to remove the const.

2015-12-13  Cary Coutant  <ccoutant@gmail.com>

        * object.h (Object::get_output_view): remove const from return type.
        (Object::do_get_output_view): Likewise.
        (Sized_relobj_file::do_get_output_view): Likewise.
        * reloc.cc (Sized_relobj_file::do_get_output_view): Likewise.

> Also, I still have an icky static_cast in the "find parameter block"
> sequence. but I suppose it's here to stay:
>
> +             Sized_relobj_file<size, true> *object_sized =
> +               static_cast<Sized_relobj_file<size, true> *>(object);

Yes, we have a few other similar casts. It's not a high priority, but
I'll eventually see about adding a virtual function to the Object
class to return a Sized_relobj_file pointer to itself.

> Btw, this patch shouldn't be landing yet, I still have to finish the gcc
> side.

No problem; let me know when it's ready.

-cary

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-13 22:42         ` Cary Coutant
@ 2015-12-13 22:43           ` Cary Coutant
  0 siblings, 0 replies; 11+ messages in thread
From: Cary Coutant @ 2015-12-13 22:43 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

[-- Attachment #1: Type: text/plain, Size: 1470 bytes --]

And with the patch...

-cary

On Sun, Dec 13, 2015 at 2:42 PM, Cary Coutant <ccoutant@gmail.com> wrote:
>> Thanks for the patches!  I've updated mine, and the changes are now limitted
>> to s390-specific code.  The output_view patch has a minor problem: it
>> returns a const unsigned char *, but I need a read-write view (I have to
>> bump the frame size in the parameter block).  I've just const_casted around
>> it for now, but I suppose we should change the return type instead?
>
> Oops, sorry! I've committed the attached patch to remove the const.
>
> 2015-12-13  Cary Coutant  <ccoutant@gmail.com>
>
>         * object.h (Object::get_output_view): remove const from return type.
>         (Object::do_get_output_view): Likewise.
>         (Sized_relobj_file::do_get_output_view): Likewise.
>         * reloc.cc (Sized_relobj_file::do_get_output_view): Likewise.
>
>> Also, I still have an icky static_cast in the "find parameter block"
>> sequence. but I suppose it's here to stay:
>>
>> +             Sized_relobj_file<size, true> *object_sized =
>> +               static_cast<Sized_relobj_file<size, true> *>(object);
>
> Yes, we have a few other similar casts. It's not a high priority, but
> I'll eventually see about adding a virtual function to the Object
> class to return a Sized_relobj_file pointer to itself.
>
>> Btw, this patch shouldn't be landing yet, I still have to finish the gcc
>> side.
>
> No problem; let me know when it's ready.
>
> -cary

[-- Attachment #2: get-output-view.patch --]
[-- Type: application/octet-stream, Size: 2724 bytes --]

2015-12-13  Cary Coutant  <ccoutant@gmail.com>

gold/
	* object.h (Object::get_output_view): remove const from return type.
	(Object::do_get_output_view): Likewise.
	(Sized_relobj_file::do_get_output_view): Likewise.
	* reloc.cc (Sized_relobj_file::do_get_output_view): Likewise.

diff --git a/gold/object.h b/gold/object.h
index 6cb82c7..a15509a 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -845,7 +845,7 @@ class Object
   { return this->do_get_incremental_reloc_count(symndx); }
 
   // Return the output view for section SHNDX.
-  const unsigned char*
+  unsigned char*
   get_output_view(unsigned int shndx, section_size_type* plen) const
   { return this->do_get_output_view(shndx, plen); }
 
@@ -1035,7 +1035,7 @@ class Object
   { gold_unreachable(); }
 
   // Return the output view for a section.
-  virtual const unsigned char*
+  virtual unsigned char*
   do_get_output_view(unsigned int, section_size_type*) const
   { gold_unreachable(); }
 
@@ -2574,7 +2574,7 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
   { this->output_local_symbol_count_ = value; }
 
   // Return the output view for a section.
-  const unsigned char*
+  unsigned char*
   do_get_output_view(unsigned int, section_size_type*) const;
 
  private:
diff --git a/gold/reloc.cc b/gold/reloc.cc
index c0c06c5..0b45514 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -1061,7 +1061,7 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections(
 // Return the output view for section SHNDX.
 
 template<int size, bool big_endian>
-const unsigned char*
+unsigned char*
 Sized_relobj_file<size, big_endian>::do_get_output_view(
     unsigned int shndx,
     section_size_type* plen) const
@@ -1775,7 +1775,7 @@ Sized_relobj_file<32, false>::do_relocate_sections(
     Views* pviews);
 
 template
-const unsigned char*
+unsigned char*
 Sized_relobj_file<32, false>::do_get_output_view(
     unsigned int shndx,
     section_size_type* plen) const;
@@ -1792,7 +1792,7 @@ Sized_relobj_file<32, true>::do_relocate_sections(
     Views* pviews);
 
 template
-const unsigned char*
+unsigned char*
 Sized_relobj_file<32, true>::do_get_output_view(
     unsigned int shndx,
     section_size_type* plen) const;
@@ -1809,7 +1809,7 @@ Sized_relobj_file<64, false>::do_relocate_sections(
     Views* pviews);
 
 template
-const unsigned char*
+unsigned char*
 Sized_relobj_file<64, false>::do_get_output_view(
     unsigned int shndx,
     section_size_type* plen) const;
@@ -1826,7 +1826,7 @@ Sized_relobj_file<64, true>::do_relocate_sections(
     Views* pviews);
 
 template
-const unsigned char*
+unsigned char*
 Sized_relobj_file<64, true>::do_get_output_view(
     unsigned int shndx,
     section_size_type* plen) const;

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] [RFC] [GOLD] s390 -fsplit-stack support.
  2015-12-10  6:25 ` Cary Coutant
  2015-12-11 21:10   ` Cary Coutant
@ 2016-03-17 22:58   ` Cary Coutant
  1 sibling, 0 replies; 11+ messages in thread
From: Cary Coutant @ 2016-03-17 22:58 UTC (permalink / raw)
  To: Marcin Kościelnicki; +Cc: Binutils

>> diff --git a/configure b/configure
>> index 3bb1c03..91fd098 100755
>> --- a/configure
>> +++ b/configure
>> @@ -2972,7 +2972,7 @@ case "${ENABLE_GOLD}" in
>>        # Check for target supported by gold.
>>        case "${target}" in
>>          i?86-*-* | x86_64-*-* | sparc*-*-* | powerpc*-*-* | arm*-*-* \
>> -        | aarch64*-*-* | tilegx*-*-*)
>> +        | aarch64*-*-* | tilegx*-*-* | s390*-*-*)
>>    configdirs="$configdirs gold"
>>    if test x${ENABLE_GOLD} = xdefault; then
>>      default_ld=gold
>
> Please remove this change from the patch. I'll get the top-level
> configure changes in separately for s390 and mips.

The top-level configure changes for both s390 and mips are committed now.

-cary

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-03-17 22:58 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-09 15:15 [PATCH] [RFC] [GOLD] s390 -fsplit-stack support Marcin Kościelnicki
2015-12-10  6:25 ` Cary Coutant
2015-12-11 21:10   ` Cary Coutant
2015-12-11 21:30     ` Marcin Kościelnicki
2015-12-11 21:58       ` Cary Coutant
2015-12-11 22:25     ` Cary Coutant
2015-12-11 22:27     ` Cary Coutant
2015-12-13  2:09       ` Marcin Kościelnicki
2015-12-13 22:42         ` Cary Coutant
2015-12-13 22:43           ` Cary Coutant
2016-03-17 22:58   ` Cary Coutant

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).