diff mbox series

gold: aarch64 split stack support

Message ID 1518026504-22473-1-git-send-email-adhemerval.zanella@linaro.org
State New
Headers show
Series gold: aarch64 split stack support | expand

Commit Message

Adhemerval Zanella Netto Feb. 7, 2018, 6:01 p.m. UTC
Changes from previous version [1]:

  - Improved the loop to detect the split-stack prologue, now it should
    avoid unbound ending and fail malformatted instruction sequences.

  - Add split_aarch64_4 tests.

--

This patch adds gold support for split-stack function calling non-split
function on aarch64.

gold/ChangeLog:

	* gold/aarch64.c (AArch64_insn_utilities::aarch64_movn_decode_imm):
	New function.
	(AArch64_insn_utilities::aarch64_movk_decode_imm): Likewise.
	(AArch64_insn_utilities::clz_hwi): Likewise.
	(AArch64_insn_utilities::aarch64_bitmask_imm): Likewise.
	(Target_aarch64::do_calls_non_split): Likewise.
	* gold/testsuite/Makefile.am [DEFAULT_TARGET_AARCH64] (check_SCRIPTS):
	add split_aarch64.sh.
	[DEFAULT_TARGET_AARCH64] (check_DATA): Add split_aarch64_{1,2,3,4,r)
	tests.
	[DEFAULT_TARGET_AARCH64] (SPLIT_DEFSYMS): New rule.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_1.o): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_2.o): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_3.o): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_4.o): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_r.o): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_n.o): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_1): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_2): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_2.stdout): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_3.stdout): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_4): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_4.stdout): Likewise.
	[DEFAULT_TARGET_AARCH64] (split_aarch64_r.stdout): Likewise.
	[DEFAULT_TARGET_AARCH64] (MOSTLYCLEANFILES): Likewise.
	* gold/testsuite/split_aarch64.sh: New file.
	* gold/testsuite/split_aarch64_1.s: Likewise.
	* gold/testsuite/split_aarch64_2.s: Likewise.
	* gold/testsuite/split_aarch64_3.s: Likewise.
	* gold/testsuite/split_aarch64_4.s: Likewise.
	* gold/testsuite/split_aarch64_n.s: Likewise.

[1] https://sourceware.org/ml/binutils/2017-07/msg00362.html
---
 gold/ChangeLog                   |  34 +++++++
 gold/aarch64.cc                  | 212 ++++++++++++++++++++++++++++++++++++++-
 gold/testsuite/Makefile.am       |  37 ++++++-
 gold/testsuite/Makefile.in       |  40 +++++++-
 gold/testsuite/split_aarch64.sh  |  60 +++++++++++
 gold/testsuite/split_aarch64_1.s |  45 +++++++++
 gold/testsuite/split_aarch64_2.s |  81 +++++++++++++++
 gold/testsuite/split_aarch64_3.s |  24 +++++
 gold/testsuite/split_aarch64_4.s |  25 +++++
 gold/testsuite/split_aarch64_n.s |  12 +++
 10 files changed, 563 insertions(+), 7 deletions(-)
 create mode 100755 gold/testsuite/split_aarch64.sh
 create mode 100644 gold/testsuite/split_aarch64_1.s
 create mode 100644 gold/testsuite/split_aarch64_2.s
 create mode 100644 gold/testsuite/split_aarch64_3.s
 create mode 100644 gold/testsuite/split_aarch64_4.s
 create mode 100644 gold/testsuite/split_aarch64_n.s

-- 
2.7.4

Comments

Cary Coutant July 12, 2018, 11:22 p.m. UTC | #1
> This patch adds gold support for split-stack function calling non-split

> function on aarch64.


>  if DEFAULT_TARGET_AARCH64

>

> -check_SCRIPTS += aarch64_reloc_none.sh

> -check_DATA += aarch64_reloc_none.stdout

> +check_SCRIPTS += aarch64_reloc_none.sh split_aarch64.sh

> +check_DATA += aarch64_reloc_none.stdout split_aarch64_1.stdout \

> +       split_aarch64_2.stdout split_aarch64_3.stdout split_aarch64_4.stdout \

> +       split_aarch64_r.stdout

>  aarch64_reloc_none.o: aarch64_reloc_none.s

>         $(TEST_AS) -o $@ $<

>  aarch64_reloc_none: aarch64_reloc_none.o ../ld-new

>         ../ld-new -o $@ aarch64_reloc_none.o --gc-sections

>  aarch64_reloc_none.stdout: aarch64_reloc_none

>         $(TEST_NM) $< > $@

> -

> -MOSTLYCLEANFILES += aarch64_reloc_none


Let's leave the aarch64_reloc_none test case untouched, and add your
new stuff below it:

+check_SCRIPTS += split_aarch64.sh
+check_DATA += split_aarch64_1.stdout \
+       split_aarch64_2.stdout split_aarch64_3.stdout split_aarch64_4.stdout \
+       split_aarch64_r.stdout

> +SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200

> +split_aarch64_1.o: split_aarch64_1.s

> +       $(TEST_AS) -o $@ $<

> +split_aarch64_2.o: split_aarch64_2.s

> +       $(TEST_AS) -o $@ $<

> +split_aarch64_3.o: split_aarch64_3.s

> +       $(TEST_AS) -o $@ $<

> +split_aarch64_4.o: split_aarch64_4.s

> +       $(TEST_AS) -o $@ $<

> +split_aarch64_n.o: split_aarch64_n.s

> +       $(TEST_AS) -o $@ $<

> +split_aarch64_1: split_aarch64_1.o ../ld-new

> +       ../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_1.o

> +split_aarch64_1.stdout: split_aarch64_1

> +       $(TEST_OBJDUMP) -d $< > $@

> +split_aarch64_2: split_aarch64_2.o split_aarch64_n.o ../ld-new

> +       ../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_2.o split_aarch64_n.o

> +split_aarch64_2.stdout: split_aarch64_2

> +       $(TEST_OBJDUMP) -d $< > $@

> +split_aarch64_3.stdout: split_aarch64_3.o split_aarch64_n.o ../ld-new

> +       ../ld-new $(SPLIT_DEFSYMS) -o split_aarch64_3 split_aarch64_3.o split_aarch64_n.o > $@ 2>&1 || exit 0

> +split_aarch64_4: split_aarch64_4.o split_aarch64_n.o ../ld-new

> +       ../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_4.o split_aarch64_n.o

> +split_aarch64_4.stdout: split_aarch64_4

> +       $(TEST_OBJDUMP) -d $< > $@

> +split_aarch64_r.stdout: split_aarch64_1.o split_aarch64_n.o ../ld-new

> +       ../ld-new -r split_aarch64_1.o split_aarch64_n.o -o split_aarch64_r > $@ 2>&1 || exit 0

> +#MOSTLYCLEANFILES += aarch64_reloc_none split_aarch64_1 split_aarch64_2 \

> +       split_aarch64_3 split_aarch64_r


Uncomment this line (and remove aarch64_reloc_none).

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

> index bbf8dc1..602e3ff 100644

> --- a/gold/testsuite/Makefile.in

> +++ b/gold/testsuite/Makefile.in


In the future, please remove the diffs for generated files when
posting patches for review.

Other than that, it looks OK.

Sorry for the delay -- I just lost track of the patch, and only found
it again today. It's always a good idea to ping me if you haven't
heard from me for a couple of weeks!

-cary
diff mbox series

Patch

diff --git a/gold/aarch64.cc b/gold/aarch64.cc
index 1abdb27..1b834d6 100644
--- a/gold/aarch64.cc
+++ b/gold/aarch64.cc
@@ -69,7 +69,7 @@  class AArch64_relocate_functions;
 
 // Utility class dealing with insns. This is ported from macros in
 // bfd/elfnn-aarch64.cc, but wrapped inside a class as static members. This
-// class is used in erratum sequence scanning.
+// class is used in erratum sequence scanning and split-stack calls.
 
 template<bool big_endian>
 class AArch64_insn_utilities
@@ -167,6 +167,98 @@  public:
     return ((((uint64_t)(1) << 32) - msbt) << 33) | value;
   }
 
+  // Retrieve encoded movn 64-bit signed imm value (64-bit variant only).
+  static int64_t
+  aarch64_movn_decode_imm(const Insntype movn)
+  {
+    int64_t imm = (movn & 0x1fffe0) >> 5;
+    int hw = ((movn & 0x600000) >> 21) << 4;
+    return imm << hw;
+  }
+
+  // Retrieve encoded movk 64-bit signed imm value updating and existent value
+  // (64-bit variant only).
+  static int64_t
+  aarch64_movk_decode_imm(const Insntype movk, int64_t value)
+  {
+    int64_t imm = (movk & 0x1FFFE0) >> 5;
+    int hw = ((movk & 0x600000) >> 21) << 4;
+    int64_t ret = imm << hw;
+    int64_t mask = 0xffff << hw;
+    return value ^ ((value ^ ret) & mask);
+  }
+
+  // Return true if val is an immediate that can be loaded into a
+  // register by a MOVZ instruction.
+  static bool
+  aarch64_movw_imm (int64_t val)
+  {
+    return ((val & 0xffffl) == val)
+            || ((val & (0xffffl << 16)) == val)
+            || ((val & (0xffffl << 32)) == val)
+            || ((val & (0xffffl << 48)) == val);
+  }
+
+  // For convenience, define 0 -> word_size.
+  static inline int
+  clz_hwi (uint64_t x)
+  {
+    if (x == 0)
+      return 64;
+    return __builtin_clzl (x);
+  }
+
+  // Return true if val is a valid bitmask immediate.
+  static bool
+  aarch64_bitmask_imm (int64_t val_in)
+  {
+    static const uint64_t bitmask_imm_mul[] =
+      {
+	0x0000000100000001ull,
+	0x0001000100010001ull,
+	0x0101010101010101ull,
+	0x1111111111111111ull,
+	0x5555555555555555ull,
+      };
+    uint64_t val, tmp, mask, first_one, next_one;
+    int bits;
+
+    // Check for a single sequence of one bits and return quickly if so.
+    // The special cases of all ones and all zeroes returns false.
+    val = (uint64_t) val_in;
+    tmp = val + (val & -val);
+
+    if (tmp == (tmp & -tmp))
+      return (val + 1) > 1;
+
+    // Invert if the immediate doesn't start with a zero bit - this means we
+    // only need to search for sequences of one bits.
+    if (val & 1)
+      val = ~val;
+
+    // Find the first set bit and set tmp to val with the first sequence of
+    // one bits removed.  Return success if there is a single sequence of
+    // ones.
+    first_one = val & -val;
+    tmp = val & (val + first_one);
+
+    if (tmp == 0)
+      return true;
+
+    // Find the next set bit and compute the difference in bit position.
+    next_one = tmp & -tmp;
+    bits = clz_hwi (first_one) - clz_hwi (next_one);
+    mask = val ^ tmp;
+
+    // Check the bit position difference is a power of 2, and that the first
+    // sequence of one bits fits within 'bits' bits.
+    if ((mask >> bits) != 0 || bits != (bits & -bits))
+      return false;
+
+    // Check the sequence of one bits is repeated 64/bits times.
+    return val == mask * bitmask_imm_mul[__builtin_clz (bits) - 26];
+  }
+
   static bool
   aarch64_b(const Insntype insn)
   { return (insn & 0xFC000000) == 0x14000000; }
@@ -3054,6 +3146,14 @@  class Target_aarch64 : public Sized_target<size, big_endian>
   do_can_check_for_function_pointers() const
   { return true; }
 
+  // Adjust -fsplit-stack code which calls non-split-stack code.
+  void
+  do_calls_non_split(Relobj* object, unsigned int shndx,
+		     section_offset_type fnoffset, section_size_type fnsize,
+		     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 number of entries in the PLT.
   unsigned int
   plt_entry_count() const;
@@ -6844,6 +6944,116 @@  Target_aarch64<size, big_endian>::gc_process_relocs(
     plocal_symbols);
 }
 
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fsplit-stack.  The function calls non-split-stack
+// code.  Change the function to ensure it has enough stack space to
+// call some random function.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::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
+{
+  // 32-bit not supported.
+  if (size == 32)
+    {
+      // warn
+      Target::do_calls_non_split(object, shndx, fnoffset, fnsize,
+				 prelocs, reloc_count, view, view_size,
+				 from, to);
+      return;
+    }
+
+  // The function starts with:
+  // mrs    x9, tpidr_el0
+  // mov    x10, <required stack allocation>
+  // movk   x10, #0x0, lsl #16
+  // sub    x10, sp, x10
+  // mov    x11, sp          # if function has stacked arguments
+  // mov    x12, x30
+  // ldur   x9, [x9, #-8]
+  // cmp    x9, x10
+
+  unsigned char *entry = view + fnoffset;
+  unsigned char *pinsn = entry;
+  bool ok = false;
+
+  const uint32_t mov_mask = 0xff80001f; // ignore 'hw' bits.
+  const uint32_t mrs_x9_tp = 0xd53bd049;
+  const uint32_t movz_x10_imm = 0xd280000a;
+  const uint32_t movk_x10_imm = 0xf280000a;
+  const uint32_t cmp_x10_mask = 0xeb00015f;
+  const size_t max_insn = 8;
+
+  uint32_t insn = elfcpp::Swap<32, big_endian>::readval(entry);
+  if (insn == mrs_x9_tp)
+    {
+      unsigned char *pmov = 0, *pmovk = 0;
+      int64_t allocate = 0;
+      /* Add an upper limit to handle ill formated headers.  */
+      for (size_t i = 0; i < max_insn; i++)
+	{
+	  pinsn += 4;
+	  insn = elfcpp::Swap<32, big_endian>::readval(pinsn);
+	  if ((insn & mov_mask) == movz_x10_imm)
+	    {
+	      pmov = pinsn;
+	      allocate = Insn_utilities::aarch64_movn_decode_imm(insn);
+	    }
+	  else if ((insn & mov_mask) == movk_x10_imm)
+	    {
+	      pmovk = pinsn;
+	      allocate = Insn_utilities::aarch64_movk_decode_imm(insn, allocate);
+	    }
+	  else if ((insn & cmp_x10_mask) == cmp_x10_mask)
+	    break;
+	}
+
+      if (pmov != 0 && pmovk != 0)
+	{
+	  int extra = parameters->options().split_stack_adjust_size();
+	  allocate += extra;
+	  if (allocate >= ((int64_t)1 << 32) || extra < 0)
+	    {
+	      object->error(_("split-stack stack size overflow at "
+			      "section %u offset %0zx"),
+			    shndx, static_cast<size_t>(fnoffset));
+	      return;
+	    }
+
+	  insn = movz_x10_imm | ((allocate & 0xffff) << 5);
+	  elfcpp::Swap<32, big_endian>::writeval(pmov, insn);
+	  insn = movk_x10_imm | (((allocate >> 16) & 0xffff) << 5);
+	  insn |= 0x200000; // Set 'hw' bits to 01 to shift imm 16 bits.
+	  elfcpp::Swap<32, big_endian>::writeval(pmovk, insn);
+	  ok = true;
+        }
+    }
+
+  if (!ok)
+    {
+      if (!object->has_no_split_stack())
+	object->error(_("failed to match split-stack sequence at "
+			"section %u offset %0zx"),
+		      shndx, static_cast<size_t>(fnoffset));
+    }
+
+  // We have to change the function so that it calls
+  // __morestack_non_split instead of __morestack.  The former will
+  // allocate additional stack space.
+  *from = "__morestack";
+  *to = "__morestack_non_split";
+}
+
 // Scan relocations for a section.
 
 template<int size, bool big_endian>
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index 16cae80..487f1f4 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -3875,16 +3875,45 @@  endif DEFAULT_TARGET_ARM
 
 if DEFAULT_TARGET_AARCH64
 
-check_SCRIPTS += aarch64_reloc_none.sh
-check_DATA += aarch64_reloc_none.stdout
+check_SCRIPTS += aarch64_reloc_none.sh split_aarch64.sh
+check_DATA += aarch64_reloc_none.stdout split_aarch64_1.stdout \
+	split_aarch64_2.stdout split_aarch64_3.stdout split_aarch64_4.stdout \
+	split_aarch64_r.stdout
 aarch64_reloc_none.o: aarch64_reloc_none.s
 	$(TEST_AS) -o $@ $<
 aarch64_reloc_none: aarch64_reloc_none.o ../ld-new
 	../ld-new -o $@ aarch64_reloc_none.o --gc-sections
 aarch64_reloc_none.stdout: aarch64_reloc_none
 	$(TEST_NM) $< > $@
-
-MOSTLYCLEANFILES += aarch64_reloc_none
+SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
+split_aarch64_1.o: split_aarch64_1.s
+	$(TEST_AS) -o $@ $<
+split_aarch64_2.o: split_aarch64_2.s
+	$(TEST_AS) -o $@ $<
+split_aarch64_3.o: split_aarch64_3.s
+	$(TEST_AS) -o $@ $<
+split_aarch64_4.o: split_aarch64_4.s
+	$(TEST_AS) -o $@ $<
+split_aarch64_n.o: split_aarch64_n.s
+	$(TEST_AS) -o $@ $<
+split_aarch64_1: split_aarch64_1.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_1.o
+split_aarch64_1.stdout: split_aarch64_1
+	$(TEST_OBJDUMP) -d $< > $@
+split_aarch64_2: split_aarch64_2.o split_aarch64_n.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_2.o split_aarch64_n.o
+split_aarch64_2.stdout: split_aarch64_2
+	$(TEST_OBJDUMP) -d $< > $@
+split_aarch64_3.stdout: split_aarch64_3.o split_aarch64_n.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o split_aarch64_3 split_aarch64_3.o split_aarch64_n.o > $@ 2>&1 || exit 0
+split_aarch64_4: split_aarch64_4.o split_aarch64_n.o ../ld-new
+	../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_4.o split_aarch64_n.o
+split_aarch64_4.stdout: split_aarch64_4
+	$(TEST_OBJDUMP) -d $< > $@
+split_aarch64_r.stdout: split_aarch64_1.o split_aarch64_n.o ../ld-new
+	../ld-new -r split_aarch64_1.o split_aarch64_n.o -o split_aarch64_r > $@ 2>&1 || exit 0
+#MOSTLYCLEANFILES += aarch64_reloc_none split_aarch64_1 split_aarch64_2 \
+	split_aarch64_3 split_aarch64_r
 
 check_SCRIPTS += aarch64_relocs.sh
 check_DATA += aarch64_relocs.stdout
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index bbf8dc1..602e3ff 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -1012,16 +1012,23 @@  check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	arm_target2_abs \
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	arm_target2_got_rel \
 @DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	arm_target_lazy_init
+#MOSTLYCLEANFILES += aarch64_reloc_none split_aarch64_1 split_aarch64_2 \
+#	split_aarch64_3 split_aarch64_r
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_100 = aarch64_reloc_none.sh \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_aarch64.sh \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	aarch64_relocs.sh \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	pr21430.sh \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	aarch64_tlsdesc.sh
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_101 = aarch64_reloc_none.stdout \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_aarch64_1.stdout \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_aarch64_2.stdout \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_aarch64_3.stdout \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_aarch64_4.stdout \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	split_aarch64_r.stdout \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	aarch64_relocs.stdout \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	pr21430.stdout \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	aarch64_tlsdesc.stdout
-@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_102 = aarch64_reloc_none \
-@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	aarch64_relocs \
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_102 = aarch64_relocs \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	pr21430 \
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	aarch64_tlsdesc
 @DEFAULT_TARGET_S390_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_103 = split_s390.sh
@@ -3438,6 +3445,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_DEPENDENCIES = gcctestdir/ld exception_x86_64_bnd_1.o exception_x86_64_bnd_2.o
 @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@exception_x86_64_bnd_test_LDFLAGS = $(exception_test_LDFLAGS) -Wl,-z,bndplt
 @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_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200
 @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
@@ -5389,6 +5397,8 @@  arm_target2_got_rel.sh.log: arm_target2_got_rel.sh
 	@p='arm_target2_got_rel.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 aarch64_reloc_none.sh.log: aarch64_reloc_none.sh
 	@p='aarch64_reloc_none.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
+split_aarch64.sh.log: split_aarch64.sh
+	@p='split_aarch64.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 aarch64_relocs.sh.log: aarch64_relocs.sh
 	@p='aarch64_relocs.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
 pr21430.sh.log: pr21430.sh
@@ -7999,6 +8009,32 @@  uninstall-am:
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new -o $@ aarch64_reloc_none.o --gc-sections
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@aarch64_reloc_none.stdout: aarch64_reloc_none
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_NM) $< > $@
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_1.o: split_aarch64_1.s
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_2.o: split_aarch64_2.s
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_3.o: split_aarch64_3.s
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_4.o: split_aarch64_4.s
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_n.o: split_aarch64_n.s
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -o $@ $<
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_1: split_aarch64_1.o ../ld-new
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_1.o
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_1.stdout: split_aarch64_1
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_2: split_aarch64_2.o split_aarch64_n.o ../ld-new
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_2.o split_aarch64_n.o
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_2.stdout: split_aarch64_2
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_3.stdout: split_aarch64_3.o split_aarch64_n.o ../ld-new
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o split_aarch64_3 split_aarch64_3.o split_aarch64_n.o > $@ 2>&1 || exit 0
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_4: split_aarch64_4.o split_aarch64_n.o ../ld-new
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new $(SPLIT_DEFSYMS) -o $@ split_aarch64_4.o split_aarch64_n.o
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_4.stdout: split_aarch64_4
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_OBJDUMP) -d $< > $@
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@split_aarch64_r.stdout: split_aarch64_1.o split_aarch64_n.o ../ld-new
+@DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	../ld-new -r split_aarch64_1.o split_aarch64_n.o -o split_aarch64_r > $@ 2>&1 || exit 0
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@aarch64_globals.o: aarch64_globals.s
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@	$(TEST_AS) -o $@ $<
 @DEFAULT_TARGET_AARCH64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@aarch64_relocs.o: aarch64_relocs.s
diff --git a/gold/testsuite/split_aarch64.sh b/gold/testsuite/split_aarch64.sh
new file mode 100755
index 0000000..3b49ae6
--- /dev/null
+++ b/gold/testsuite/split_aarch64.sh
@@ -0,0 +1,60 @@ 
+#!/bin/sh
+
+# split_aarch64.sh -- test -fstack-split for AArch64
+
+# Copyright (C) 2009-2017 Free Software Foundation, Inc.
+
+# 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 'mov.*+x10,.#0x0( |$)' split_aarch64_1.stdout
+match 'mov.*+x10,.#0x0,.lsl.#16( |$)' split_aarch64_1.stdout
+match 'b.*__morestack>?$' split_aarch64_1.stdout
+
+match 'mov.*+x10,.#0x4000( |$)' split_aarch64_2.stdout
+match 'mov.*+x10,.#0x0,.lsl.#16( |$)' split_aarch64_2.stdout
+match 'b.*__morestack_non_split>?$' split_aarch64_2.stdout
+match 'mov.*+x10,.#0x4210( |$)' split_aarch64_2.stdout
+match 'mov.*+x10,.#0x0,.lsl.#16( |$)' split_aarch64_2.stdout
+match 'b.*__morestack_non_split>?$' split_aarch64_2.stdout
+match 'mov.*+x10,.#0x4110( |$)' split_aarch64_2.stdout
+match 'mov.*+x10,.#0x1,.lsl.#16( |$)' split_aarch64_2.stdout
+match 'b.*__morestack_non_split>?$' split_aarch64_2.stdout
+
+match 'failed to match' split_aarch64_3.stdout
+
+match 'mov.*+x10,.#0x0( |$)' split_aarch64_4.stdout
+match 'mov.*+x10,.#0x0,.lsl.#16( |$)' split_aarch64_4.stdout
+match 'b.*__morestack_non_split>?$' split_aarch64_2.stdout
+
+match 'cannot mix' split_aarch64_r.stdout
diff --git a/gold/testsuite/split_aarch64_1.s b/gold/testsuite/split_aarch64_1.s
new file mode 100644
index 0000000..fd09987
--- /dev/null
+++ b/gold/testsuite/split_aarch64_1.s
@@ -0,0 +1,45 @@ 
+# split_aarch64_1.s: Split aware function calling each other, no need to
+# adjust stack or change the morestack symbol call.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	mrs     x9, tpidr_el0
+	mov     x10, 0
+	movk    x10, 0x0, lsl 16
+	sub     x10, sp, x10
+	adrp    x12, .L2
+	add     x12, x12, :lo12:.L2
+	ldr     x9, [x9, -8]
+	cmp     x9, x10
+	blt     .Lbcond4
+	b       __morestack
+.Lbcond4:
+.L2:
+	b       fn2
+	.size	fn1,. - fn1
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	mrs     x9, tpidr_el0
+	mov     x10, 528
+	movk    x10, 0x0, lsl 16
+	sub     x10, sp, x10
+	adrp    x12, .L3
+	add     x12, x12, :lo12:.L3
+	ldr     x9, [x9, -8]
+	cmp     x9, x10
+	blt     .Lbcond5
+	b       __morestack
+.Lbcond5:
+.L3:
+	bl      fn1
+	ret
+
+	.size	fn2,. - fn2
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_aarch64_2.s b/gold/testsuite/split_aarch64_2.s
new file mode 100644
index 0000000..9819d43
--- /dev/null
+++ b/gold/testsuite/split_aarch64_2.s
@@ -0,0 +1,81 @@ 
+# split_aarch64_2.s: Split aware function calling non-split.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	mrs     x9, tpidr_el0
+	mov     x10, 0
+	movk    x10, 0x0, lsl 16
+	sub     x10, sp, x10
+	adrp    x12, .L2
+	add     x12, x12, :lo12:.L2
+	ldr     x9, [x9, -8]
+	cmp     x9, x10
+	blt     .Lbcond4
+	b       __morestack
+.Lbcond4:
+.L2:
+	b       fnn
+	.size	fn1,. - fn1
+
+	.global	fn2
+	.type	fn2,@function
+fn2:
+	mrs     x9, tpidr_el0
+	mov     x10, 528
+	movk    x10, 0x0, lsl 16
+	sub     x10, sp, x10
+	adrp    x12, .L6
+	add     x12, x12, :lo12:.L6
+	ldr     x9, [x9, -8]
+	cmp     x9, x10
+	blt     .Lbcond8
+	b       __morestack
+.Lbcond8:
+.L6:
+	bl      fnn
+	ret
+	.size	fn2,. - fn2
+
+	.global fn3
+	.type	fn3,@function
+fn3:
+        mrs     x9, tpidr_el0
+        mov     x10, 272
+        movk    x10, 0x1, lsl 16
+        sub     x10, sp, x10
+        adrp    x12, .L10
+        add     x12, x12, :lo12:.L10
+        ldr     x9, [x9, -8]
+        cmp     x9, x10
+        blt     .Lbcond12
+        b       __morestack
+.Lbcond12:
+.L10:
+        bl      fnn
+        ret
+        .size   fn3, .-fn3
+
+	.global fn4
+	.type	fn4,@function
+fn4:
+        mrs     x9, tpidr_el0
+        mov     x10, 65520
+        movk    x10, 0x1, lsl 16
+        sub     x10, sp, x10
+        adrp    x12, .L14
+        add     x12, x12, :lo12:.L14
+        ldr     x9, [x9, -8]
+        cmp     x9, x10
+        blt     .Lbcond16
+        b       __morestack
+.Lbcond16:
+.L14:
+        bl      fnn
+        ret
+        .size   fn4, .-fn4
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_aarch64_3.s b/gold/testsuite/split_aarch64_3.s
new file mode 100644
index 0000000..c32db61
--- /dev/null
+++ b/gold/testsuite/split_aarch64_3.s
@@ -0,0 +1,24 @@ 
+# split_aarch64_3.s: Missing initial thread-pointer get instruction.
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	mov     x10, 0
+	movk    x10, 0x0, lsl 16
+	sub     x10, sp, x10
+	adrp    x12, .L2
+	add     x12, x12, :lo12:.L2
+	ldr     x9, [x9, -8]
+	cmp     x9, x10
+	blt     .Lbcond4
+	b       __morestack
+.Lbcond4:
+.L2:
+	b       fnn
+
+	.size	fn1,. - fn1
+
+	.section	.note.GNU-stack,"",@progbits
+	.section	.note.GNU-split-stack,"",@progbits
diff --git a/gold/testsuite/split_aarch64_4.s b/gold/testsuite/split_aarch64_4.s
new file mode 100644
index 0000000..f2988a5
--- /dev/null
+++ b/gold/testsuite/split_aarch64_4.s
@@ -0,0 +1,25 @@ 
+# split_aarch64_3.s: permitted adjustment failure
+
+	.text
+
+	.global	fn1
+	.type	fn1,@function
+fn1:
+	mov     x10, 0
+	movk    x10, 0x0, lsl 16
+	sub     x10, sp, x10
+	adrp    x12, .L2
+	add     x12, x12, :lo12:.L2
+	ldr     x9, [x9, -8]
+	cmp     x9, x10
+	blt     .Lbcond4
+	b       __morestack
+.Lbcond4:
+.L2:
+	b       fnn
+
+	.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_aarch64_n.s b/gold/testsuite/split_aarch64_n.s
new file mode 100644
index 0000000..31c29cf
--- /dev/null
+++ b/gold/testsuite/split_aarch64_n.s
@@ -0,0 +1,12 @@ 
+# split_aarch64_n.s: AArch64 specific, -fsplit-stack calling non-split
+
+	.text
+
+	.global	fnn
+	.type	fnn,@function
+fnn:
+	ret
+
+	.size	fnn,. - fnn
+
+	.section	.note.GNU-stack,"",@progbits