1 | /* Test if an executable can read from the TLS from an STT_GNU_IFUNC resolver. |
2 | Copyright (C) 2017-2022 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 |
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 |
58 | init_foo (void) |
59 | { |
60 | bar_ptr = &bar_gd; |
61 | } |
62 | |
63 | int |
64 | my_foo (void) |
65 | { |
66 | printf (format: "&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 |
73 | init_tcb_test (void) |
74 | { |
75 | resolver_platform = get_platform (); |
76 | } |
77 | |
78 | int |
79 | my_tcb_test (void) |
80 | { |
81 | printf (format: "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 |
90 | do_test (void) |
91 | { |
92 | int ret = 0; |
93 | |
94 | if (foo ()) |
95 | printf (format: "PASS: foo IFUNC resolver called once.\n" ); |
96 | else |
97 | { |
98 | printf (format: "FAIL: foo IFUNC resolver not called once.\n" ); |
99 | ret = 1; |
100 | } |
101 | |
102 | if (&bar == bar_ptr) |
103 | printf (format: "PASS: bar address read from IFUNC resolver is correct.\n" ); |
104 | else |
105 | { |
106 | printf (format: "FAIL: bar address read from IFUNC resolver is incorrect.\n" ); |
107 | ret = 1; |
108 | } |
109 | |
110 | if (tcb_test ()) |
111 | printf (format: "PASS: tcb_test IFUNC resolver called once.\n" ); |
112 | else |
113 | { |
114 | printf (format: "FAIL: tcb_test IFUNC resolver not called once.\n" ); |
115 | ret = 1; |
116 | } |
117 | |
118 | if (resolver_platform == get_platform ()) |
119 | printf (format: "PASS: platform read from IFUNC resolver is correct.\n" ); |
120 | else |
121 | { |
122 | printf (format: "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 | |