1 /* Copyright (C) 2002-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #include <assert.h>
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <stdio.h>
22 #include <stdio_ext.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/resource.h>
26 #include "pthreadP.h"
27 #include <lowlevellock.h>
28 #include <ldsodefs.h>
29
30
31 int
__pthread_getattr_np(pthread_t thread_id,pthread_attr_t * attr)32 __pthread_getattr_np (pthread_t thread_id, pthread_attr_t *attr)
33 {
34 struct pthread *thread = (struct pthread *) thread_id;
35
36 /* Prepare the new thread attribute. */
37 int ret = __pthread_attr_init (attr);
38 if (ret != 0)
39 return ret;
40
41 struct pthread_attr *iattr = (struct pthread_attr *) attr;
42
43 lll_lock (thread->lock, LLL_PRIVATE);
44
45 /* The thread library is responsible for keeping the values in the
46 thread desriptor up-to-date in case the user changes them. */
47 memcpy (&iattr->schedparam, &thread->schedparam,
48 sizeof (struct sched_param));
49 iattr->schedpolicy = thread->schedpolicy;
50
51 /* Clear the flags work. */
52 iattr->flags = thread->flags;
53
54 /* The thread might be detached by now. */
55 if (IS_DETACHED (thread))
56 iattr->flags |= ATTR_FLAG_DETACHSTATE;
57
58 /* This is the guardsize after adjusting it. */
59 iattr->guardsize = thread->reported_guardsize;
60
61 /* The sizes are subject to alignment. */
62 if (__glibc_likely (thread->stackblock != NULL))
63 {
64 /* The stack size reported to the user should not include the
65 guard size. */
66 iattr->stacksize = thread->stackblock_size - thread->guardsize;
67 #if _STACK_GROWS_DOWN
68 iattr->stackaddr = (char *) thread->stackblock
69 + thread->stackblock_size;
70 #else
71 iattr->stackaddr = (char *) thread->stackblock;
72 #endif
73 }
74 else
75 {
76 /* No stack information available. This must be for the initial
77 thread. Get the info in some magical way. */
78
79 /* Stack size limit. */
80 struct rlimit rl;
81
82 /* The safest way to get the top of the stack is to read
83 /proc/self/maps and locate the line into which
84 __libc_stack_end falls. */
85 FILE *fp = fopen ("/proc/self/maps", "rce");
86 if (fp == NULL)
87 ret = errno;
88 /* We need the limit of the stack in any case. */
89 else
90 {
91 if (__getrlimit (RLIMIT_STACK, &rl) != 0)
92 ret = errno;
93 else
94 {
95 /* We consider the main process stack to have ended with
96 the page containing __libc_stack_end. There is stuff below
97 it in the stack too, like the program arguments, environment
98 variables and auxv info, but we ignore those pages when
99 returning size so that the output is consistent when the
100 stack is marked executable due to a loaded DSO requiring
101 it. */
102 void *stack_end = (void *) ((uintptr_t) __libc_stack_end
103 & -(uintptr_t) GLRO(dl_pagesize));
104 #if _STACK_GROWS_DOWN
105 stack_end += GLRO(dl_pagesize);
106 #endif
107 /* We need no locking. */
108 __fsetlocking (fp, FSETLOCKING_BYCALLER);
109
110 /* Until we found an entry (which should always be the case)
111 mark the result as a failure. */
112 ret = ENOENT;
113
114 char *line = NULL;
115 size_t linelen = 0;
116 #if _STACK_GROWS_DOWN
117 uintptr_t last_to = 0;
118 #endif
119
120 while (! feof_unlocked (fp))
121 {
122 if (__getline (&line, &linelen, fp) <= 0)
123 break;
124
125 uintptr_t from;
126 uintptr_t to;
127 if (sscanf (line, "%" SCNxPTR "-%" SCNxPTR, &from, &to) != 2)
128 continue;
129 if (from <= (uintptr_t) __libc_stack_end
130 && (uintptr_t) __libc_stack_end < to)
131 {
132 /* Found the entry. Now we have the info we need. */
133 iattr->stackaddr = stack_end;
134 iattr->stacksize =
135 rl.rlim_cur - (size_t) (to - (uintptr_t) stack_end);
136
137 /* Cut it down to align it to page size since otherwise we
138 risk going beyond rlimit when the kernel rounds up the
139 stack extension request. */
140 iattr->stacksize = (iattr->stacksize
141 & -(intptr_t) GLRO(dl_pagesize));
142 #if _STACK_GROWS_DOWN
143 /* The limit might be too high. */
144 if ((size_t) iattr->stacksize
145 > (size_t) iattr->stackaddr - last_to)
146 iattr->stacksize = (size_t) iattr->stackaddr - last_to;
147 #else
148 /* The limit might be too high. */
149 if ((size_t) iattr->stacksize
150 > to - (size_t) iattr->stackaddr)
151 iattr->stacksize = to - (size_t) iattr->stackaddr;
152 #endif
153 /* We succeed and no need to look further. */
154 ret = 0;
155 break;
156 }
157 #if _STACK_GROWS_DOWN
158 last_to = to;
159 #endif
160 }
161
162 free (line);
163 }
164
165 fclose (fp);
166 }
167 }
168
169 iattr->flags |= ATTR_FLAG_STACKADDR;
170
171 if (ret == 0)
172 {
173 size_t size = 16;
174 cpu_set_t *cpuset = NULL;
175
176 do
177 {
178 size <<= 1;
179
180 void *newp = realloc (cpuset, size);
181 if (newp == NULL)
182 {
183 ret = ENOMEM;
184 break;
185 }
186 cpuset = (cpu_set_t *) newp;
187
188 ret = __pthread_getaffinity_np (thread_id, size, cpuset);
189 }
190 /* Pick some ridiculous upper limit. Is 8 million CPUs enough? */
191 while (ret == EINVAL && size < 1024 * 1024);
192
193 if (ret == 0)
194 ret = __pthread_attr_setaffinity_np (attr, size, cpuset);
195 else if (ret == ENOSYS)
196 /* There is no such functionality. */
197 ret = 0;
198 free (cpuset);
199 }
200
201 lll_unlock (thread->lock, LLL_PRIVATE);
202
203 if (ret != 0)
204 __pthread_attr_destroy (attr);
205
206 return ret;
207 }
208 versioned_symbol (libc, __pthread_getattr_np, pthread_getattr_np, GLIBC_2_32);
209
210 #if SHLIB_COMPAT (libc, GLIBC_2_2_3, GLIBC_2_32)
211 strong_alias (__pthread_getattr_np, __pthread_getattr_np_alias)
212 compat_symbol (libc, __pthread_getattr_np_alias,
213 pthread_getattr_np, GLIBC_2_2_3);
214 #endif
215