@@ -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)
+ {
+ 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;
+ }
+
+ gold_assert(pmov);
+ gold_assert(pmovk);
+
+ if (insn == 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>
@@ -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
new file mode 100755
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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