1 /* Test if an executable can read from the TLS from an STT_GNU_IFUNC resolver.
2    Copyright (C) 2017-2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <inttypes.h>
23 #include <libc-symbols.h>
24 
25 __thread int bar;
26 extern __thread int bar_gd asm ("bar") __attribute__ ((tls_model("global-dynamic")));
27 static int *bar_ptr = NULL;
28 
29 static uint32_t resolver_platform = 0;
30 
31 int foo (void);
32 
33 int tcb_test (void);
34 
35 /* Offsets copied from tcb-offsets.h.  */
36 #ifdef __powerpc64__
37 # define __TPREG     "r13"
38 # define __ATPLATOFF -28764
39 #else
40 # define __TPREG     "r2"
41 # define __ATPLATOFF -28724
42 #endif
43 
44 uint32_t
get_platform(void)45 get_platform (void)
46 {
47   register unsigned long tp __asm__ (__TPREG);
48   uint32_t tmp;
49 
50   __asm__  ("lwz %0,%1(%2)\n"
51 	    : "=r" (tmp)
52 	    : "n" (__ATPLATOFF), "b" (tp));
53 
54   return tmp;
55 }
56 
57 void
init_foo(void)58 init_foo (void)
59 {
60   bar_ptr = &bar_gd;
61 }
62 
63 int
my_foo(void)64 my_foo (void)
65 {
66   printf ("&bar = %p and bar_ptr = %p.\n", &bar, bar_ptr);
67   return bar_ptr != NULL;
68 }
69 
70 __ifunc (foo, foo, my_foo, void, init_foo);
71 
72 void
init_tcb_test(void)73 init_tcb_test (void)
74 {
75   resolver_platform = get_platform ();
76 }
77 
78 int
my_tcb_test(void)79 my_tcb_test (void)
80 {
81   printf ("resolver_platform = 0x%"PRIx32
82 	  " and current platform = 0x%"PRIx32".\n",
83 	  resolver_platform, get_platform ());
84   return resolver_platform != 0;
85 }
86 
87 __ifunc (tcb_test, tcb_test, my_tcb_test, void, init_tcb_test);
88 
89 static int
do_test(void)90 do_test (void)
91 {
92   int ret = 0;
93 
94   if (foo ())
95     printf ("PASS: foo IFUNC resolver called once.\n");
96   else
97     {
98       printf ("FAIL: foo IFUNC resolver not called once.\n");
99       ret = 1;
100     }
101 
102   if (&bar == bar_ptr)
103     printf ("PASS: bar address read from IFUNC resolver is correct.\n");
104   else
105     {
106       printf ("FAIL: bar address read from IFUNC resolver is incorrect.\n");
107       ret = 1;
108     }
109 
110   if (tcb_test ())
111     printf ("PASS: tcb_test IFUNC resolver called once.\n");
112   else
113     {
114       printf ("FAIL: tcb_test IFUNC resolver not called once.\n");
115       ret = 1;
116     }
117 
118   if (resolver_platform == get_platform ())
119     printf ("PASS: platform read from IFUNC resolver is correct.\n");
120   else
121     {
122       printf ("FAIL: platform read from IFUNC resolver is incorrect.\n");
123       ret = 1;
124     }
125 
126   return ret;
127 }
128 
129 #include <support/test-driver.c>
130