This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [PATCH] gold: aarch64 split stack support
- From: "Han Shen via binutils" <binutils at sourceware dot org>
- To: adhemerval dot zanella at linaro dot org, binutils <binutils at sourceware dot org>
- Cc: Cary Coutant <ccoutant at gmail dot com>
- Date: Tue, 15 Aug 2017 16:25:36 -0700
- Subject: Re: [PATCH] gold: aarch64 split stack support
- Authentication-results: sourceware.org; auth=none
- Reply-to: Han Shen <shenhan at google dot com>
Hi Adhemerval, thanks for working on this. Here are a few comments -
On Thu, Aug 3, 2017 at 10:35 PM, Cary Coutant <ccoutant@gmail.com> wrote:
> Han, can you take a look at this?
>
> https://sourceware.org/ml/binutils/2017-07/msg00362.html
>
> -cary
>
>
>
> ---------- Forwarded message ----------
> From: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> Date: Mon, Jul 31, 2017 at 12:03 PM
> Subject: [PATCH] gold: aarch64 split stack support
> To: binutils@sourceware.org
>
>
> This patch adds gold support for split-stack function calling non-split
> function on aarch64 following current proposal idea [1] [2].
>
> 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://gcc.gnu.org/ml/gcc-patches/2017-07/msg01717.html
> [2] https://sourceware.org/ml/libc-alpha/2017-02/msg00272.html
> ---
> gold/ChangeLog | 34 +++++++
> gold/aarch64.cc | 213 ++++++++++++++++++++++++++++++++++++++-
> gold/testsuite/Makefile.am | 37 ++++++-
> gold/testsuite/Makefile.in | 40 +++++++-
> gold/testsuite/split_aarch64.sh | 59 +++++++++++
> 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_n.s | 12 +++
> 9 files changed, 538 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_n.s
>
> diff --git a/gold/aarch64.cc b/gold/aarch64.cc
> index 58c7449..8220a45 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; }
> @@ -3009,6 +3101,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;
> @@ -6799,6 +6899,117 @@ 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
> + // adrp x12, main_fn_entry
> + // add x12, x12, :lo12:.L2
> + // cmp x9, x10
> +
> + unsigned char *entry = view + fnoffset;
> + unsigned char *pinsn = entry;
> + bool ok = false;
> +
> + unsigned char *pmov = 0, *pmovk = 0;
> + 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 sub_x10_sp_x10 = 0xcb2a63ea;
> +
> + uint32_t insn = elfcpp::Swap<32, big_endian>::readval(entry);
> + if (insn == mrs_x9_tp)
> + {
> + int64_t allocate = 0;
> + while (1)
> + {
This seems a little bit fragile to me - what if a function starts with
insn "mrs x9, tpidr_el0", but the rest of the function does not match
the rest of the pattern (for example, in a hand-written asm file),
this would lead to a loop that never ends.
> + 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 == sub_x10_sp_x10)
> + break;
This is the only loop exit, but we may never find "sub_x10_sp_x10"
insn in some hand written code.
> + }
> +
> + gold_assert(pmov);
> + gold_assert(pmovk);
> +
> + if (insn == sub_x10_sp_x10)
When control flow reaches here, insn always equals to sub_x10_sp_x10.
> + {
> + 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 26ee77a..8bcd198 100644
> --- a/gold/testsuite/Makefile.am
> +++ b/gold/testsuite/Makefile.am
> @@ -3794,16 +3794,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/split_aarch64.sh b/gold/testsuite/split_aarch64.sh
> new file mode 100755
> index 0000000..7d12c88
> --- /dev/null
> +++ b/gold/testsuite/split_aarch64.sh
> @@ -0,0 +1,59 @@
> +#!/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,.#0x4000( |$)' split_aarch64_4.stdout
> +match 'mov.*+x10,.#0x0,.lsl.#16( |$)' split_aarch64_4.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_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
> --
> 2.7.4
--
Han Shen | Software Engineer | shenhan@google.com | +1-650-440-3330