Branch data Line data Source code
1 : : /* Declarations for common convenience functions.
2 : : Copyright (C) 2006-2011 Red Hat, Inc.
3 : : Copyright (C) 2022, 2026 Mark J. Wielaard <mark@klomp.org>
4 : : Copyright (C) 2023 Khem Raj.
5 : : This file is part of elfutils.
6 : :
7 : : This file is free software; you can redistribute it and/or modify
8 : : it under the terms of either
9 : :
10 : : * the GNU Lesser General Public License as published by the Free
11 : : Software Foundation; either version 3 of the License, or (at
12 : : your option) any later version
13 : :
14 : : or
15 : :
16 : : * the GNU General Public License as published by the Free
17 : : Software Foundation; either version 2 of the License, or (at
18 : : your option) any later version
19 : :
20 : : or both in parallel, as here.
21 : :
22 : : elfutils is distributed in the hope that it will be useful, but
23 : : WITHOUT ANY WARRANTY; without even the implied warranty of
24 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 : : General Public License for more details.
26 : :
27 : : You should have received copies of the GNU General Public License and
28 : : the GNU Lesser General Public License along with this program. If
29 : : not, see <http://www.gnu.org/licenses/>. */
30 : :
31 : : #ifndef LIB_SYSTEM_H
32 : : #define LIB_SYSTEM_H 1
33 : :
34 : : /* Prevent double inclusion of config.h, config.h includes eu-config.h. */
35 : : #ifdef HAVE_CONFIG_H
36 : : #ifndef EU_CONFIG_H
37 : : # include <config.h>
38 : : #endif
39 : : #endif
40 : :
41 : : #include <errno.h>
42 : : #include <fcntl.h>
43 : : #include <stdbool.h>
44 : : #include <stddef.h>
45 : : #include <stdint.h>
46 : : #include <string.h>
47 : : #include <stdarg.h>
48 : : #include <stdlib.h>
49 : :
50 : : /* System dependent headers */
51 : : #include <byteswap.h>
52 : : #include <endian.h>
53 : : #include <sys/mman.h>
54 : : #include <sys/param.h>
55 : : #include <unistd.h>
56 : :
57 : : #if defined(HAVE_SYS_RANDOM_H)
58 : : #include <sys/random.h>
59 : : #endif
60 : :
61 : : #if defined(HAVE_ERROR_H)
62 : : #include <error.h>
63 : : #elif defined(HAVE_ERR_H)
64 : : extern int error_message_count;
65 : : void error(int status, int errnum, const char *format, ...);
66 : : #else
67 : : #error "err.h or error.h must be available"
68 : : #endif
69 : :
70 : : /* error (EXIT_FAILURE, ...) should be noreturn but on some systems it
71 : : isn't. This may cause warnings about code that should not be reachable.
72 : : So have an explicit error_exit wrapper that is noreturn (because it
73 : : calls exit explicitly). */
74 : : #define error_exit(errnum,...) do { \
75 : : error (EXIT_FAILURE,errnum,__VA_ARGS__); \
76 : : exit (EXIT_FAILURE); \
77 : : } while (0)
78 : :
79 : : #if BYTE_ORDER == LITTLE_ENDIAN
80 : : # define LE32(n) (n)
81 : : # define LE64(n) (n)
82 : : # define BE32(n) bswap_32 (n)
83 : : # define BE64(n) bswap_64 (n)
84 : : #elif BYTE_ORDER == BIG_ENDIAN
85 : : # define BE32(n) (n)
86 : : # define BE64(n) (n)
87 : : # define LE32(n) bswap_32 (n)
88 : : # define LE64(n) bswap_64 (n)
89 : : #else
90 : : # error "Unknown byte order"
91 : : #endif
92 : :
93 : : #ifndef MAX
94 : : #define MAX(m, n) ((m) < (n) ? (n) : (m))
95 : : #endif
96 : :
97 : : #ifndef MIN
98 : : #define MIN(m, n) ((m) < (n) ? (m) : (n))
99 : : #endif
100 : :
101 : : #if !HAVE_DECL_POWEROF2
102 : : #define powerof2(x) (((x) & ((x) - 1)) == 0)
103 : : #endif
104 : :
105 : : #if !HAVE_DECL_MEMPCPY
106 : : #define mempcpy(dest, src, n) \
107 : : ((void *) ((char *) memcpy (dest, src, n) + (size_t) n))
108 : : #endif
109 : :
110 : : #if !HAVE_DECL_REALLOCARRAY
111 : : static inline void *
112 : : reallocarray (void *ptr, size_t nmemb, size_t size)
113 : : {
114 : : if (size > 0 && nmemb > SIZE_MAX / size)
115 : : {
116 : : errno = ENOMEM;
117 : : return NULL;
118 : : }
119 : : return realloc (ptr, nmemb * size);
120 : : }
121 : : #endif
122 : :
123 : : /* Return TRUE if the start of STR matches PREFIX, FALSE otherwise. */
124 : :
125 : : static inline int
126 : 13933955 : startswith (const char *str, const char *prefix)
127 : : {
128 : 13933955 : return strncmp (str, prefix, strlen (prefix)) == 0;
129 : : }
130 : :
131 : : /* Return TRUE if STR[FROM] is a valid string with a zero terminator
132 : : at or before STR[TO - 1]. Note FROM is an index into the STR
133 : : array, while TO is the maximum size of the STR array. This
134 : : function returns FALSE when TO is zero or FROM >= TO. */
135 : : static inline bool
136 : 12114196 : validate_str (const char *str, size_t from, size_t to)
137 : : {
138 : : #if HAVE_DECL_MEMRCHR
139 : : // Check end first, which is likely a zero terminator,
140 : : // to prevent function call
141 : 12114196 : return (to > 0
142 [ + - - + ]: 12114196 : && (str[to - 1] == '\0'
143 [ # # ]: 0 : || (to > from
144 [ # # ]: 0 : && memrchr (&str[from], '\0', to - from - 1) != NULL)));
145 : : #else
146 : : do {
147 : : if (to <= from)
148 : : return false;
149 : :
150 : : to--;
151 : : } while (str[to]);
152 : :
153 : : return true;
154 : : #endif
155 : : }
156 : :
157 : : /* A special gettext function we use if the strings are too short. */
158 : : #define sgettext(Str) \
159 : : ({ const char *__res = strrchr (_(Str), '|'); \
160 : : __res ? __res + 1 : Str; })
161 : :
162 : : #define gettext_noop(Str) Str
163 : :
164 : : #ifndef TEMP_FAILURE_RETRY
165 : : #define TEMP_FAILURE_RETRY(expression) \
166 : : ({ ssize_t __res; \
167 : : do \
168 : : __res = expression; \
169 : : while (__res == -1 && errno == EINTR); \
170 : : __res; })
171 : : #endif
172 : :
173 : : #ifndef ACCESSPERMS
174 : : #define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO) /* 0777 */
175 : : #endif
176 : :
177 : : #ifndef ALLPERMS
178 : : #define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* 07777 */
179 : : #endif
180 : :
181 : : #ifndef DEFFILEMODE
182 : : #define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)/* 0666 */
183 : : #endif
184 : :
185 : : static inline ssize_t __attribute__ ((unused))
186 : 2126406 : pwrite_retry (int fd, const void *buf, size_t len, off_t off)
187 : : {
188 : 2126406 : ssize_t recvd = 0;
189 : :
190 : 2126406 : do
191 : : {
192 [ - + - - ]: 2126406 : ssize_t ret = TEMP_FAILURE_RETRY (pwrite (fd, ((char *)buf) + recvd, len - recvd,
193 : : off + recvd));
194 [ + + ]: 2126406 : if (ret <= 0)
195 [ + - ]: 506 : return ret < 0 ? ret : recvd;
196 : :
197 : 2125900 : recvd += ret;
198 : : }
199 [ - + ]: 2125900 : while ((size_t) recvd < len);
200 : :
201 : : return recvd;
202 : : }
203 : :
204 : : static inline ssize_t __attribute__ ((unused))
205 : 310 : write_retry (int fd, const void *buf, size_t len)
206 : : {
207 : 310 : ssize_t recvd = 0;
208 : :
209 : 310 : do
210 : : {
211 [ - + - - ]: 310 : ssize_t ret = TEMP_FAILURE_RETRY (write (fd, ((char *)buf) + recvd, len - recvd));
212 [ - + ]: 310 : if (ret <= 0)
213 [ # # ]: 0 : return ret < 0 ? ret : recvd;
214 : :
215 : 310 : recvd += ret;
216 : : }
217 [ - + ]: 310 : while ((size_t) recvd < len);
218 : :
219 : : return recvd;
220 : : }
221 : :
222 : : static inline ssize_t __attribute__ ((unused))
223 : 1644472 : pread_retry (int fd, void *buf, size_t len, off_t off)
224 : : {
225 : 1644472 : ssize_t recvd = 0;
226 : :
227 : 1654498 : do
228 : : {
229 [ + + - + : 3308996 : ssize_t ret = TEMP_FAILURE_RETRY (pread (fd, ((char *)buf) + recvd, len - recvd,
- - ]
230 : : off + recvd));
231 [ + + ]: 1654498 : if (ret <= 0)
232 [ + - ]: 10068 : return ret < 0 ? ret : recvd;
233 : :
234 : 1644430 : recvd += ret;
235 : : }
236 [ + + ]: 1644430 : while ((size_t) recvd < len);
237 : :
238 : : return recvd;
239 : : }
240 : :
241 : : static inline ssize_t __attribute__ ((unused))
242 : 0 : read_retry (int fd, void *buf, size_t len)
243 : : {
244 : 0 : ssize_t recvd = 0;
245 : :
246 : 0 : do
247 : : {
248 [ # # # # : 0 : ssize_t ret = TEMP_FAILURE_RETRY (read (fd, ((char *)buf) + recvd,
# # ]
249 : : len - recvd));
250 [ # # ]: 0 : if (ret <= 0)
251 [ # # ]: 0 : return ret < 0 ? ret : recvd;
252 : :
253 : 0 : recvd += ret;
254 : : }
255 [ # # ]: 0 : while ((size_t) recvd < len);
256 : :
257 : : return recvd;
258 : : }
259 : :
260 : : /* The demangler from libstdc++. */
261 : : extern char *__cxa_demangle (const char *mangled_name, char *output_buffer,
262 : : size_t *length, int *status);
263 : :
264 : : /* A static assertion. This will cause a compile-time error if EXPR,
265 : : which must be a compile-time constant, is false. */
266 : :
267 : : #define eu_static_assert(expr) \
268 : : extern int never_defined_just_used_for_checking[(expr) ? 1 : -1] \
269 : : __attribute__ ((unused))
270 : :
271 : : /* We really want a basename implementation that doesn't modify the
272 : : input argument. Normally you get that from string.h with _GNU_SOURCE
273 : : define. But some libc implementations don't define it and other
274 : : define it, but provide an implementation that still modifies the
275 : : argument. So define our own and poison a bare basename symbol. */
276 : : static inline const char *
277 : 21586 : xbasename(const char *s)
278 : : {
279 : 21586 : const char *p = strrchr(s, '/');
280 [ + + ]: 21586 : return p ? p+1 : s;
281 : : }
282 : : #pragma GCC poison basename
283 : :
284 : : /* Get a random uint64_t. Returns zero on success, minus one on failure. */
285 : : static inline int
286 : 92 : xrandom64 (uint64_t *r)
287 : : {
288 : : /* Prefer getentropy if it is available, fallback to getrandom, if
289 : : both are missing, or if they fail try reading from /dev/urandom. */
290 : : #if HAVE_DECL_GETENTROPY
291 [ + - ]: 92 : if (getentropy (r, sizeof (*r)) == 0)
292 : : return 0;
293 : : #elif HAVE_DECL_GETRANDOM
294 : : if (TEMP_FAILURE_RETRY (getrandom (r, sizeof (*r), 0)) == sizeof (*r))
295 : : return 0;
296 : : #endif
297 : 0 : int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
298 [ # # ]: 0 : if (fd < 0)
299 : : return -1;
300 [ # # ]: 0 : if (read_retry (fd, r, sizeof (uint64_t)) == sizeof (uint64_t))
301 : : {
302 : 0 : close (fd);
303 : 0 : return 0;
304 : : }
305 : 0 : int save_errno = errno;
306 : 0 : close (fd);
307 : 0 : errno = save_errno;
308 : : /* We could try some pseudo-random thing with getpid and
309 : : clock_gettime. But if even getting something from /dev/urandom
310 : : fails it seems we tried hard enough already. */
311 : 0 : return -1;
312 : : }
313 : :
314 : : /* There is no mkstempat needed for creating a temp file in a specific
315 : : directory. Needed e.g. in combination with renameat to atomicly
316 : : replace a file. So define one ourselves. Like mkstemp the template
317 : : must end in "XXXXXX", which are replaced by an unique filename
318 : : suffix. The file is created with user read/write permissions only
319 : : in the given dirfd using openat.
320 : : https://sourceware.org/bugzilla/show_bug.cgi?id=19866 */
321 : : static inline int
322 : 92 : xmkstempat (int dirfd, char *templ)
323 : : {
324 : : /* Only use these 64 chars. */
325 : 92 : const char chars[] =
326 : : "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
327 : :
328 : : /* Must end in 6X. */
329 : 92 : size_t l = strlen (templ);
330 [ + - - + ]: 92 : if (l < 6 || memcmp (templ + l - 6, "XXXXXX", 6) != 0)
331 : : {
332 : 0 : errno = EINVAL;
333 : 0 : return -1;
334 : : }
335 : :
336 : : int tries = 128; /* Just fail with EEXIST if 128 tries wasn't enough. */
337 : 92 : do
338 : : {
339 : 92 : uint64_t r; /* We need at least 64^6 == 2^36 */
340 [ + - ]: 92 : if (xrandom64 (&r) != 0)
341 : 92 : return -1;
342 : :
343 : : /* Random chars for the template. */
344 [ + + ]: 644 : for (int i = 0; i < 6; i++)
345 : : {
346 : 552 : templ[l - 6 + i] = chars[r % 64];
347 : 552 : r /= 64;
348 : : }
349 : :
350 : : /* Must be able to open exclusively. */
351 : 92 : int fd = openat (dirfd, templ,
352 : : O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC,
353 : : S_IRUSR | S_IWUSR);
354 [ - + ]: 92 : if (fd >= 0)
355 : : return fd;
356 : :
357 : 0 : tries--;
358 : : }
359 [ # # # # ]: 0 : while (tries > 0 && errno == EEXIST);
360 : :
361 : : return -1;
362 : : }
363 : :
364 : : #endif /* system.h */
|