summaryrefslogtreecommitdiff
path: root/sysdeps/powerpc/tst-tlsifunc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/powerpc/tst-tlsifunc.c')
-rw-r--r--sysdeps/powerpc/tst-tlsifunc.c129
1 files changed, 129 insertions, 0 deletions
diff --git a/sysdeps/powerpc/tst-tlsifunc.c b/sysdeps/powerpc/tst-tlsifunc.c
new file mode 100644
index 0000000000..75cfb3e656
--- /dev/null
+++ b/sysdeps/powerpc/tst-tlsifunc.c
@@ -0,0 +1,129 @@
+/* Test if an executable can read from the TLS from an STT_GNU_IFUNC resolver.
+ Copyright (C) 2017-2018 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <libc-symbols.h>
+#include <tls-macros.h>
+
+__thread int bar;
+static int *bar_ptr = NULL;
+
+static uint32_t resolver_platform = 0;
+
+int foo (void);
+
+int tcb_test (void);
+
+/* Offsets copied from tcb-offsets.h. */
+#ifdef __powerpc64__
+# define __TPREG "r13"
+# define __ATPLATOFF -28764
+#else
+# define __TPREG "r2"
+# define __ATPLATOFF -28724
+#endif
+
+uint32_t
+get_platform (void)
+{
+ register unsigned long tp __asm__ (__TPREG);
+ uint32_t tmp;
+
+ __asm__ ("lwz %0,%1(%2)\n"
+ : "=r" (tmp)
+ : "i" (__ATPLATOFF), "b" (tp));
+
+ return tmp;
+}
+
+void
+init_foo (void)
+{
+ bar_ptr = TLS_GD (bar);
+}
+
+int
+my_foo (void)
+{
+ printf ("&bar = %p and bar_ptr = %p.\n", &bar, bar_ptr);
+ return bar_ptr != NULL;
+}
+
+__ifunc (foo, foo, my_foo, void, init_foo);
+
+void
+init_tcb_test (void)
+{
+ resolver_platform = get_platform ();
+}
+
+int
+my_tcb_test (void)
+{
+ printf ("resolver_platform = 0x%"PRIx32
+ " and current platform = 0x%"PRIx32".\n",
+ resolver_platform, get_platform ());
+ return resolver_platform != 0;
+}
+
+__ifunc (tcb_test, tcb_test, my_tcb_test, void, init_tcb_test);
+
+static int
+do_test (void)
+{
+ int ret = 0;
+
+ if (foo ())
+ printf ("PASS: foo IFUNC resolver called once.\n");
+ else
+ {
+ printf ("FAIL: foo IFUNC resolver not called once.\n");
+ ret = 1;
+ }
+
+ if (&bar == bar_ptr)
+ printf ("PASS: bar address read from IFUNC resolver is correct.\n");
+ else
+ {
+ printf ("FAIL: bar address read from IFUNC resolver is incorrect.\n");
+ ret = 1;
+ }
+
+ if (tcb_test ())
+ printf ("PASS: tcb_test IFUNC resolver called once.\n");
+ else
+ {
+ printf ("FAIL: tcb_test IFUNC resolver not called once.\n");
+ ret = 1;
+ }
+
+ if (resolver_platform == get_platform ())
+ printf ("PASS: platform read from IFUNC resolver is correct.\n");
+ else
+ {
+ printf ("FAIL: platform read from IFUNC resolver is incorrect.\n");
+ ret = 1;
+ }
+
+ return ret;
+}
+
+#include <support/test-driver.c>