diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/clone.S b/sysdeps/unix/sysv/linux/s390/s390-32/clone.S
index 5d8d873383..fd1e509cf4 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/clone.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/clone.S
@@ -53,6 +53,7 @@ ENTRY(__clone)
br %r14
error:
lhi %r2,-EINVAL
+ lm %r6,%r7,24(%r15) /* Load registers. */
j SYSCALL_ERROR_LABEL
PSEUDO_END (__clone)
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/clone.S b/sysdeps/unix/sysv/linux/s390/s390-64/clone.S
index f1c4288a3d..7b37b18010 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/clone.S
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/clone.S
@@ -54,6 +54,7 @@ ENTRY(__clone)
br %r14
error:
lghi %r2,-EINVAL
+ lmg %r6,%r7,48(%r15) /* Restore registers. */
jg SYSCALL_ERROR_LABEL
PSEUDO_END (__clone)
diff --git a/sysdeps/unix/sysv/linux/tst-clone.c b/sysdeps/unix/sysv/linux/tst-clone.c
index 56348707d4..95bd0f6ccb 100644
--- a/sysdeps/unix/sysv/linux/tst-clone.c
+++ b/sysdeps/unix/sysv/linux/tst-clone.c
@@ -16,12 +16,16 @@
License along with the GNU C Library; if not, see
. */
-/* BZ #2386 */
+/* BZ #2386, BZ #31402 */
#include
#include
#include
#include
#include
+#include /* For _STACK_GROWS_{UP,DOWN}. */
+#include
+
+volatile unsigned v = 0xdeadbeef;
#ifdef __ia64__
extern int __clone2 (int (*__fn) (void *__arg), void *__child_stack_base,
@@ -35,26 +39,72 @@ int child_fn(void *arg)
}
static int
-do_test (void)
+__attribute__((noinline))
+do_clone (int (*fn)(void *), void *stack)
{
int result;
+ unsigned int a = v;
+ unsigned int b = v;
+ unsigned int c = v;
+ unsigned int d = v;
+ unsigned int e = v;
+ unsigned int f = v;
+ unsigned int g = v;
+ unsigned int h = v;
+ unsigned int i = v;
+ unsigned int j = v;
+ unsigned int k = v;
+ unsigned int l = v;
+ unsigned int m = v;
+ unsigned int n = v;
+ unsigned int o = v;
#ifdef __ia64__
- result = __clone2 (child_fn, NULL, 0, 0, NULL, NULL, NULL);
+ result = __clone2 (fn, stack, stack != NULL ? 128 * 1024 : 0, 0, NULL, NULL,
+ NULL);
#else
- result = clone (child_fn, NULL, 0, NULL);
+ result = clone (fn, stack, 0, NULL);
#endif
- if (errno != EINVAL || result != -1)
- {
- printf ("FAIL: clone()=%d (wanted -1) errno=%d (wanted %d)\n",
- result, errno, EINVAL);
- return 1;
- }
+ /* Check that clone does not clobber call-saved registers. */
+ TEST_VERIFY (a == v && b == v && c == v && d == v && e == v && f == v
+ && g == v && h == v && i == v && j == v && k == v && l == v
+ && m == v && n == v && o == v);
+
+ return result;
+}
+
+static void
+__attribute__((noinline))
+do_test_single (int (*fn)(void *), void *stack)
+{
+ printf ("%s (fn=%p, stack=%p)\n", __FUNCTION__, fn, stack);
+ errno = 0;
+
+ int result = do_clone (fn, stack);
+
+ TEST_COMPARE (errno, EINVAL);
+ TEST_COMPARE (result, -1);
+}
+
+static int
+do_test (void)
+{
+ char st[128 * 1024] __attribute__ ((aligned));
+ void *stack = NULL;
+#if defined __ia64__ || _STACK_GROWS_UP
+ stack = st;
+#elif _STACK_GROWS_DOWN
+ stack = st + sizeof (st);
+#else
+# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP"
+#endif
+
+ do_test_single (child_fn, NULL);
+ do_test_single (NULL, stack);
+ do_test_single (NULL, NULL);
- puts ("All OK");
return 0;
}
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include