/src/binutils-gdb/libctf/ctf-subr.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Simple subrs. |
2 | | Copyright (C) 2019-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of libctf. |
5 | | |
6 | | libctf is free software; you can redistribute it and/or modify it under |
7 | | the terms of the GNU General Public License as published by the Free |
8 | | Software Foundation; either version 3, or (at your option) any later |
9 | | version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, but |
12 | | WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
14 | | See the GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program; see the file COPYING. If not see |
18 | | <http://www.gnu.org/licenses/>. */ |
19 | | |
20 | | #include <ctf-impl.h> |
21 | | #ifdef HAVE_MMAP |
22 | | #include <sys/mman.h> |
23 | | #endif |
24 | | #include <sys/types.h> |
25 | | #include <stdarg.h> |
26 | | #include <string.h> |
27 | | #include <unistd.h> |
28 | | |
29 | | #ifndef ENOTSUP |
30 | | #define ENOTSUP ENOSYS |
31 | | #endif |
32 | | |
33 | | int _libctf_version = CTF_VERSION; /* Library client version. */ |
34 | | int _libctf_debug = 0; /* Debugging messages enabled. */ |
35 | | |
36 | | /* Private, read-only mmap from a file, with fallback to copying. |
37 | | |
38 | | No handling of page-offset issues at all: the caller must allow for that. */ |
39 | | |
40 | | _libctf_malloc_ void * |
41 | | ctf_mmap (size_t length, size_t offset, int fd) |
42 | 0 | { |
43 | 0 | void *data; |
44 | |
|
45 | 0 | #ifdef HAVE_MMAP |
46 | 0 | data = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fd, offset); |
47 | 0 | if (data == MAP_FAILED) |
48 | 0 | data = NULL; |
49 | | #else |
50 | | if ((data = malloc (length)) != NULL) |
51 | | { |
52 | | if (ctf_pread (fd, data, length, offset) <= 0) |
53 | | { |
54 | | free (data); |
55 | | data = NULL; |
56 | | } |
57 | | } |
58 | | #endif |
59 | 0 | return data; |
60 | 0 | } |
61 | | |
62 | | void |
63 | | ctf_munmap (void *buf, size_t length _libctf_unused_) |
64 | 0 | { |
65 | 0 | #ifdef HAVE_MMAP |
66 | 0 | (void) munmap (buf, length); |
67 | | #else |
68 | | free (buf); |
69 | | #endif |
70 | 0 | } |
71 | | |
72 | | ssize_t |
73 | | ctf_pread (int fd, void *buf, ssize_t count, off_t offset) |
74 | 0 | { |
75 | 0 | ssize_t len; |
76 | 0 | size_t acc = 0; |
77 | 0 | char *data = (char *) buf; |
78 | |
|
79 | 0 | #ifdef HAVE_PREAD |
80 | 0 | while (count > 0) |
81 | 0 | { |
82 | 0 | errno = 0; |
83 | 0 | if (((len = pread (fd, data, count, offset)) < 0) && |
84 | 0 | errno != EINTR) |
85 | 0 | return len; |
86 | 0 | if (errno == EINTR) |
87 | 0 | continue; |
88 | | |
89 | 0 | acc += len; |
90 | 0 | if (len == 0) /* EOF. */ |
91 | 0 | return acc; |
92 | | |
93 | 0 | count -= len; |
94 | 0 | offset += len; |
95 | 0 | data += len; |
96 | 0 | } |
97 | 0 | return acc; |
98 | | #else |
99 | | off_t orig_off; |
100 | | |
101 | | if ((orig_off = lseek (fd, 0, SEEK_CUR)) < 0) |
102 | | return -1; |
103 | | if ((lseek (fd, offset, SEEK_SET)) < 0) |
104 | | return -1; |
105 | | |
106 | | while (count > 0) |
107 | | { |
108 | | errno = 0; |
109 | | if (((len = read (fd, data, count)) < 0) && |
110 | | errno != EINTR) |
111 | | return len; |
112 | | if (errno == EINTR) |
113 | | continue; |
114 | | |
115 | | acc += len; |
116 | | if (len == 0) /* EOF. */ |
117 | | break; |
118 | | |
119 | | count -= len; |
120 | | data += len; |
121 | | } |
122 | | if ((lseek (fd, orig_off, SEEK_SET)) < 0) |
123 | | return -1; /* offset is smashed. */ |
124 | | #endif |
125 | | |
126 | 0 | return acc; |
127 | 0 | } |
128 | | |
129 | | /* Set the CTF library client version to the specified version. If version is |
130 | | zero, we just return the default library version number. */ |
131 | | int |
132 | | ctf_version (int version) |
133 | 0 | { |
134 | 0 | if (version < 0) |
135 | 0 | { |
136 | 0 | errno = EINVAL; |
137 | 0 | return -1; |
138 | 0 | } |
139 | | |
140 | 0 | if (version > 0) |
141 | 0 | { |
142 | | /* Dynamic version switching is not presently supported. */ |
143 | 0 | if (version != CTF_VERSION) |
144 | 0 | { |
145 | 0 | errno = ENOTSUP; |
146 | 0 | return -1; |
147 | 0 | } |
148 | 0 | ctf_dprintf ("ctf_version: client using version %d\n", version); |
149 | 0 | _libctf_version = version; |
150 | 0 | } |
151 | | |
152 | 0 | return _libctf_version; |
153 | 0 | } |
154 | | |
155 | | /* Get and set CTF dict-wide flags. We are fairly strict about returning |
156 | | errors here, to make it easier to determine programmatically which flags are |
157 | | valid. */ |
158 | | |
159 | | int |
160 | | ctf_dict_set_flag (ctf_dict_t *fp, uint64_t flag, int set) |
161 | 0 | { |
162 | 0 | if (set < 0 || set > 1) |
163 | 0 | return (ctf_set_errno (fp, ECTF_BADFLAG)); |
164 | | |
165 | 0 | switch (flag) |
166 | 0 | { |
167 | 0 | case CTF_STRICT_NO_DUP_ENUMERATORS: |
168 | 0 | if (set) |
169 | 0 | fp->ctf_flags |= LCTF_STRICT_NO_DUP_ENUMERATORS; |
170 | 0 | else |
171 | 0 | fp->ctf_flags &= ~LCTF_STRICT_NO_DUP_ENUMERATORS; |
172 | 0 | break; |
173 | 0 | default: |
174 | 0 | return (ctf_set_errno (fp, ECTF_BADFLAG)); |
175 | 0 | } |
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | | int |
180 | | ctf_dict_get_flag (ctf_dict_t *fp, uint64_t flag) |
181 | 0 | { |
182 | 0 | switch (flag) |
183 | 0 | { |
184 | 0 | case CTF_STRICT_NO_DUP_ENUMERATORS: |
185 | 0 | return (fp->ctf_flags & LCTF_STRICT_NO_DUP_ENUMERATORS) != 0; |
186 | 0 | default: |
187 | 0 | return (ctf_set_errno (fp, ECTF_BADFLAG)); |
188 | 0 | } |
189 | 0 | return 0; |
190 | 0 | } |
191 | | |
192 | | void |
193 | | libctf_init_debug (void) |
194 | 0 | { |
195 | 0 | static int inited; |
196 | 0 | if (!inited) |
197 | 0 | { |
198 | 0 | _libctf_debug = getenv ("LIBCTF_DEBUG") != NULL; |
199 | 0 | inited = 1; |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | | void ctf_setdebug (int debug) |
204 | 0 | { |
205 | | /* Ensure that libctf_init_debug() has been called, so that we don't get our |
206 | | debugging-on-or-off smashed by the next call. */ |
207 | |
|
208 | 0 | libctf_init_debug(); |
209 | 0 | _libctf_debug = debug; |
210 | 0 | ctf_dprintf ("CTF debugging set to %i\n", debug); |
211 | 0 | } |
212 | | |
213 | | int ctf_getdebug (void) |
214 | 0 | { |
215 | 0 | return _libctf_debug; |
216 | 0 | } |
217 | | |
218 | | _libctf_printflike_ (1, 2) |
219 | | void ctf_dprintf (const char *format, ...) |
220 | 0 | { |
221 | 0 | if (_libctf_unlikely_ (_libctf_debug)) |
222 | 0 | { |
223 | 0 | va_list alist; |
224 | |
|
225 | 0 | va_start (alist, format); |
226 | 0 | fflush (stdout); |
227 | 0 | (void) fputs ("libctf DEBUG: ", stderr); |
228 | 0 | (void) vfprintf (stderr, format, alist); |
229 | 0 | va_end (alist); |
230 | 0 | } |
231 | 0 | } |
232 | | |
233 | | /* This needs more attention to thread-safety later on. */ |
234 | | static ctf_list_t open_errors; |
235 | | |
236 | | /* Errors and warnings. Report the warning or error to the list in FP (or the |
237 | | open errors list if NULL): if ERR is nonzero it is the errno to report to the |
238 | | debug stream instead of that recorded on fp. */ |
239 | | _libctf_printflike_ (4, 5) |
240 | | extern void |
241 | | ctf_err_warn (ctf_dict_t *fp, int is_warning, int err, |
242 | | const char *format, ...) |
243 | 0 | { |
244 | 0 | va_list alist; |
245 | 0 | ctf_err_warning_t *cew; |
246 | | |
247 | | /* Don't bother reporting errors here: we can't do much about them if they |
248 | | happen. If we're so short of memory that a tiny malloc doesn't work, a |
249 | | vfprintf isn't going to work either and the caller will have to rely on the |
250 | | ENOMEM return they'll be getting in short order anyway. */ |
251 | |
|
252 | 0 | if ((cew = malloc (sizeof (ctf_err_warning_t))) == NULL) |
253 | 0 | return; |
254 | | |
255 | 0 | cew->cew_is_warning = is_warning; |
256 | 0 | va_start (alist, format); |
257 | 0 | if (vasprintf (&cew->cew_text, format, alist) < 0) |
258 | 0 | { |
259 | 0 | free (cew); |
260 | 0 | va_end (alist); |
261 | 0 | return; |
262 | 0 | } |
263 | 0 | va_end (alist); |
264 | | |
265 | | /* Include the error code only if there is one; if this is a warning, |
266 | | only use the error code if it was explicitly passed and is nonzero. |
267 | | (Warnings may not have a meaningful error code, since the warning may not |
268 | | lead to unwinding up to the user.) */ |
269 | 0 | if ((!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0))) |
270 | 0 | || (is_warning && err != 0)) |
271 | 0 | ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("warning") : _("error"), |
272 | 0 | cew->cew_text, err != 0 ? ctf_errmsg (err) |
273 | 0 | : ctf_errmsg (ctf_errno (fp))); |
274 | 0 | else |
275 | 0 | ctf_dprintf ("%s: %s\n", is_warning ? _("warning") : _("error"), |
276 | 0 | cew->cew_text); |
277 | |
|
278 | 0 | if (fp != NULL) |
279 | 0 | ctf_list_append (&fp->ctf_errs_warnings, cew); |
280 | 0 | else |
281 | 0 | ctf_list_append (&open_errors, cew); |
282 | 0 | } |
283 | | |
284 | | /* Move all the errors/warnings from an fp into the open_errors. */ |
285 | | void |
286 | | ctf_err_warn_to_open (ctf_dict_t *fp) |
287 | 0 | { |
288 | 0 | ctf_list_splice (&open_errors, &fp->ctf_errs_warnings); |
289 | 0 | } |
290 | | |
291 | | /* Error-warning reporting: an 'iterator' that returns errors and warnings from |
292 | | the error/warning list, in order of emission. Errors and warnings are popped |
293 | | after return: the caller must free the returned error-text pointer. |
294 | | |
295 | | An fp of NULL returns CTF-open-time errors from the open_errors variable |
296 | | above. |
297 | | |
298 | | The treatment of errors from this function itself is somewhat unusual: it |
299 | | will often be called on an error path, so we don't want to overwrite the |
300 | | ctf_errno unless we have no choice. So, like ctf_bufopen et al, this |
301 | | function takes an errp pointer where errors are reported. The pointer is |
302 | | optional: if not set, errors are reported via the fp (if non-NULL). Calls |
303 | | with neither fp nor errp set are mildly problematic because there is no clear |
304 | | way to report end-of-iteration: you just have to assume that a NULL return |
305 | | means the end, and not an iterator error. */ |
306 | | |
307 | | char * |
308 | | ctf_errwarning_next (ctf_dict_t *fp, ctf_next_t **it, int *is_warning, |
309 | | int *errp) |
310 | 0 | { |
311 | 0 | ctf_next_t *i = *it; |
312 | 0 | char *ret; |
313 | 0 | ctf_list_t *errlist; |
314 | 0 | ctf_err_warning_t *cew; |
315 | |
|
316 | 0 | if (fp) |
317 | 0 | errlist = &fp->ctf_errs_warnings; |
318 | 0 | else |
319 | 0 | errlist = &open_errors; |
320 | |
|
321 | 0 | if (!i) |
322 | 0 | { |
323 | 0 | if ((i = ctf_next_create ()) == NULL) |
324 | 0 | { |
325 | 0 | if (errp) |
326 | 0 | *errp = ENOMEM; |
327 | 0 | else if (fp) |
328 | 0 | ctf_set_errno (fp, ENOMEM); |
329 | 0 | return NULL; |
330 | 0 | } |
331 | | |
332 | 0 | i->cu.ctn_fp = fp; |
333 | 0 | i->ctn_iter_fun = (void (*) (void)) ctf_errwarning_next; |
334 | 0 | *it = i; |
335 | 0 | } |
336 | | |
337 | 0 | if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun) |
338 | 0 | { |
339 | 0 | if (errp) |
340 | 0 | *errp = ECTF_NEXT_WRONGFUN; |
341 | 0 | else if (fp) |
342 | 0 | ctf_set_errno (fp, ECTF_NEXT_WRONGFUN); |
343 | 0 | return NULL; |
344 | 0 | } |
345 | | |
346 | 0 | if (fp != i->cu.ctn_fp) |
347 | 0 | { |
348 | 0 | if (errp) |
349 | 0 | *errp = ECTF_NEXT_WRONGFP; |
350 | 0 | else if (fp) |
351 | 0 | ctf_set_errno (fp, ECTF_NEXT_WRONGFP); |
352 | 0 | return NULL; |
353 | 0 | } |
354 | | |
355 | 0 | cew = ctf_list_next (errlist); |
356 | |
|
357 | 0 | if (!cew) |
358 | 0 | { |
359 | 0 | ctf_next_destroy (i); |
360 | 0 | *it = NULL; |
361 | 0 | if (errp) |
362 | 0 | *errp = ECTF_NEXT_END; |
363 | 0 | else if (fp) |
364 | 0 | ctf_set_errno (fp, ECTF_NEXT_END); |
365 | 0 | return NULL; |
366 | 0 | } |
367 | | |
368 | 0 | if (is_warning) |
369 | 0 | *is_warning = cew->cew_is_warning; |
370 | 0 | ret = cew->cew_text; |
371 | 0 | ctf_list_delete (errlist, cew); |
372 | 0 | free (cew); |
373 | 0 | return ret; |
374 | 0 | } |
375 | | |
376 | | void |
377 | | ctf_assert_fail_internal (ctf_dict_t *fp, const char *file, size_t line, |
378 | | const char *exprstr) |
379 | 0 | { |
380 | 0 | ctf_set_errno (fp, ECTF_INTERNAL); |
381 | 0 | ctf_err_warn (fp, 0, 0, _("%s: %lu: libctf assertion failed: %s"), |
382 | 0 | file, (long unsigned int) line, exprstr); |
383 | 0 | } |