xref: /netbsd-src/external/gpl3/gdb/dist/libctf/ctf-subr.c (revision c9055873d0546e63388f027d3d7f85381cde0545)
18dffb485Schristos /* Simple subrs.
2*c9055873Schristos    Copyright (C) 2019-2024 Free Software Foundation, Inc.
38dffb485Schristos 
48dffb485Schristos    This file is part of libctf.
58dffb485Schristos 
68dffb485Schristos    libctf is free software; you can redistribute it and/or modify it under
78dffb485Schristos    the terms of the GNU General Public License as published by the Free
88dffb485Schristos    Software Foundation; either version 3, or (at your option) any later
98dffb485Schristos    version.
108dffb485Schristos 
118dffb485Schristos    This program is distributed in the hope that it will be useful, but
128dffb485Schristos    WITHOUT ANY WARRANTY; without even the implied warranty of
138dffb485Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
148dffb485Schristos    See the GNU General Public License for more details.
158dffb485Schristos 
168dffb485Schristos    You should have received a copy of the GNU General Public License
178dffb485Schristos    along with this program; see the file COPYING.  If not see
188dffb485Schristos    <http://www.gnu.org/licenses/>.  */
198dffb485Schristos 
208dffb485Schristos #include <ctf-impl.h>
218dffb485Schristos #ifdef HAVE_MMAP
228dffb485Schristos #include <sys/mman.h>
238dffb485Schristos #endif
248dffb485Schristos #include <sys/types.h>
258dffb485Schristos #include <stdarg.h>
268dffb485Schristos #include <string.h>
278dffb485Schristos #include <unistd.h>
288dffb485Schristos 
298dffb485Schristos #ifndef ENOTSUP
308dffb485Schristos #define ENOTSUP ENOSYS
318dffb485Schristos #endif
328dffb485Schristos 
338dffb485Schristos int _libctf_version = CTF_VERSION;	      /* Library client version.  */
348dffb485Schristos int _libctf_debug = 0;			      /* Debugging messages enabled.  */
358dffb485Schristos 
368dffb485Schristos /* Private, read-only mmap from a file, with fallback to copying.
378dffb485Schristos 
388dffb485Schristos    No handling of page-offset issues at all: the caller must allow for that. */
398dffb485Schristos 
408dffb485Schristos _libctf_malloc_ void *
418dffb485Schristos ctf_mmap (size_t length, size_t offset, int fd)
428dffb485Schristos {
438dffb485Schristos   void *data;
448dffb485Schristos 
458dffb485Schristos #ifdef HAVE_MMAP
468dffb485Schristos   data = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fd, offset);
478dffb485Schristos   if (data == MAP_FAILED)
488dffb485Schristos     data = NULL;
498dffb485Schristos #else
508dffb485Schristos   if ((data = malloc (length)) != NULL)
518dffb485Schristos     {
528dffb485Schristos       if (ctf_pread (fd, data, length, offset) <= 0)
538dffb485Schristos 	{
548dffb485Schristos 	  free (data);
558dffb485Schristos 	  data = NULL;
568dffb485Schristos 	}
578dffb485Schristos     }
588dffb485Schristos #endif
598dffb485Schristos   return data;
608dffb485Schristos }
618dffb485Schristos 
628dffb485Schristos void
638dffb485Schristos ctf_munmap (void *buf, size_t length _libctf_unused_)
648dffb485Schristos {
658dffb485Schristos #ifdef HAVE_MMAP
668dffb485Schristos   (void) munmap (buf, length);
678dffb485Schristos #else
688dffb485Schristos   free (buf);
698dffb485Schristos #endif
708dffb485Schristos }
718dffb485Schristos 
728dffb485Schristos ssize_t
738dffb485Schristos ctf_pread (int fd, void *buf, ssize_t count, off_t offset)
748dffb485Schristos {
758dffb485Schristos   ssize_t len;
768dffb485Schristos   size_t acc = 0;
778dffb485Schristos   char *data = (char *) buf;
788dffb485Schristos 
798dffb485Schristos #ifdef HAVE_PREAD
808dffb485Schristos   while (count > 0)
818dffb485Schristos     {
828dffb485Schristos       errno = 0;
838dffb485Schristos       if (((len = pread (fd, data, count, offset)) < 0) &&
848dffb485Schristos 	  errno != EINTR)
858dffb485Schristos 	  return len;
868dffb485Schristos       if (errno == EINTR)
878dffb485Schristos 	continue;
888dffb485Schristos 
898dffb485Schristos       acc += len;
908dffb485Schristos       if (len == 0)				/* EOF.  */
918dffb485Schristos 	return acc;
928dffb485Schristos 
938dffb485Schristos       count -= len;
948dffb485Schristos       offset += len;
958dffb485Schristos       data += len;
968dffb485Schristos     }
978dffb485Schristos   return acc;
988dffb485Schristos #else
998dffb485Schristos   off_t orig_off;
1008dffb485Schristos 
1018dffb485Schristos   if ((orig_off = lseek (fd, 0, SEEK_CUR)) < 0)
1028dffb485Schristos     return -1;
1038dffb485Schristos   if ((lseek (fd, offset, SEEK_SET)) < 0)
1048dffb485Schristos     return -1;
1058dffb485Schristos 
1068dffb485Schristos   while (count > 0)
1078dffb485Schristos     {
1088dffb485Schristos       errno = 0;
1098dffb485Schristos       if (((len = read (fd, data, count)) < 0) &&
1108dffb485Schristos 	  errno != EINTR)
1118dffb485Schristos 	  return len;
1128dffb485Schristos       if (errno == EINTR)
1138dffb485Schristos 	continue;
1148dffb485Schristos 
1158dffb485Schristos       acc += len;
1168dffb485Schristos       if (len == 0)				/* EOF.  */
1178dffb485Schristos 	break;
1188dffb485Schristos 
1198dffb485Schristos       count -= len;
1208dffb485Schristos       data += len;
1218dffb485Schristos     }
1228dffb485Schristos   if ((lseek (fd, orig_off, SEEK_SET)) < 0)
1238dffb485Schristos     return -1;					/* offset is smashed.  */
1248dffb485Schristos #endif
1258dffb485Schristos 
1268dffb485Schristos   return acc;
1278dffb485Schristos }
1288dffb485Schristos 
1298dffb485Schristos /* Set the CTF library client version to the specified version.  If version is
1308dffb485Schristos    zero, we just return the default library version number.  */
1318dffb485Schristos int
1328dffb485Schristos ctf_version (int version)
1338dffb485Schristos {
1348dffb485Schristos   if (version < 0)
1358dffb485Schristos     {
1368dffb485Schristos       errno = EINVAL;
1378dffb485Schristos       return -1;
1388dffb485Schristos     }
1398dffb485Schristos 
1408dffb485Schristos   if (version > 0)
1418dffb485Schristos     {
1428dffb485Schristos       /*  Dynamic version switching is not presently supported. */
1438dffb485Schristos       if (version != CTF_VERSION)
1448dffb485Schristos 	{
1458dffb485Schristos 	  errno = ENOTSUP;
1468dffb485Schristos 	  return -1;
1478dffb485Schristos 	}
1488dffb485Schristos       ctf_dprintf ("ctf_version: client using version %d\n", version);
1498dffb485Schristos       _libctf_version = version;
1508dffb485Schristos     }
1518dffb485Schristos 
1528dffb485Schristos   return _libctf_version;
1538dffb485Schristos }
1548dffb485Schristos 
1558dffb485Schristos void
1568dffb485Schristos libctf_init_debug (void)
1578dffb485Schristos {
1588dffb485Schristos   static int inited;
1598dffb485Schristos   if (!inited)
1608dffb485Schristos     {
1618dffb485Schristos       _libctf_debug = getenv ("LIBCTF_DEBUG") != NULL;
1628dffb485Schristos       inited = 1;
1638dffb485Schristos     }
1648dffb485Schristos }
1658dffb485Schristos 
1668dffb485Schristos void ctf_setdebug (int debug)
1678dffb485Schristos {
1688dffb485Schristos   /* Ensure that libctf_init_debug() has been called, so that we don't get our
1698dffb485Schristos      debugging-on-or-off smashed by the next call.  */
1708dffb485Schristos 
1718dffb485Schristos   libctf_init_debug();
1728dffb485Schristos   _libctf_debug = debug;
1738dffb485Schristos   ctf_dprintf ("CTF debugging set to %i\n", debug);
1748dffb485Schristos }
1758dffb485Schristos 
1768dffb485Schristos int ctf_getdebug (void)
1778dffb485Schristos {
1788dffb485Schristos   return _libctf_debug;
1798dffb485Schristos }
1808dffb485Schristos 
1818dffb485Schristos _libctf_printflike_ (1, 2)
1828dffb485Schristos void ctf_dprintf (const char *format, ...)
1838dffb485Schristos {
1848dffb485Schristos   if (_libctf_unlikely_ (_libctf_debug))
1858dffb485Schristos     {
1868dffb485Schristos       va_list alist;
1878dffb485Schristos 
1888dffb485Schristos       va_start (alist, format);
1898dffb485Schristos       fflush (stdout);
1908dffb485Schristos       (void) fputs ("libctf DEBUG: ", stderr);
1918dffb485Schristos       (void) vfprintf (stderr, format, alist);
1928dffb485Schristos       va_end (alist);
1938dffb485Schristos     }
1948dffb485Schristos }
1958dffb485Schristos 
1968dffb485Schristos /* This needs more attention to thread-safety later on.  */
1978dffb485Schristos static ctf_list_t open_errors;
1988dffb485Schristos 
1998dffb485Schristos /* Errors and warnings.  Report the warning or error to the list in FP (or the
2008dffb485Schristos    open errors list if NULL): if ERR is nonzero it is the errno to report to the
2018dffb485Schristos    debug stream instead of that recorded on fp.  */
2028dffb485Schristos _libctf_printflike_ (4, 5)
2038dffb485Schristos extern void
2044b169a6bSchristos ctf_err_warn (ctf_dict_t *fp, int is_warning, int err,
2058dffb485Schristos 	      const char *format, ...)
2068dffb485Schristos {
2078dffb485Schristos   va_list alist;
2088dffb485Schristos   ctf_err_warning_t *cew;
2098dffb485Schristos 
2108dffb485Schristos   /* Don't bother reporting errors here: we can't do much about them if they
2118dffb485Schristos      happen.  If we're so short of memory that a tiny malloc doesn't work, a
2128dffb485Schristos      vfprintf isn't going to work either and the caller will have to rely on the
2138dffb485Schristos      ENOMEM return they'll be getting in short order anyway.  */
2148dffb485Schristos 
2158dffb485Schristos   if ((cew = malloc (sizeof (ctf_err_warning_t))) == NULL)
2168dffb485Schristos     return;
2178dffb485Schristos 
2188dffb485Schristos   cew->cew_is_warning = is_warning;
2198dffb485Schristos   va_start (alist, format);
2208dffb485Schristos   if (vasprintf (&cew->cew_text, format, alist) < 0)
2218dffb485Schristos     {
2228dffb485Schristos       free (cew);
2238dffb485Schristos       va_end (alist);
2248dffb485Schristos       return;
2258dffb485Schristos     }
2268dffb485Schristos   va_end (alist);
2278dffb485Schristos 
228*c9055873Schristos   /* Include the error code only if there is one; if this is a warning,
2294b169a6bSchristos      only use the error code if it was explicitly passed and is nonzero.
2308dffb485Schristos      (Warnings may not have a meaningful error code, since the warning may not
2318dffb485Schristos      lead to unwinding up to the user.)  */
2324b169a6bSchristos   if ((!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0)))
2334b169a6bSchristos       || (is_warning && err != 0))
2348dffb485Schristos     ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("error") : _("warning"),
2358dffb485Schristos 		 cew->cew_text, err != 0 ? ctf_errmsg (err)
2368dffb485Schristos 		 : ctf_errmsg (ctf_errno (fp)));
2378dffb485Schristos   else
2388dffb485Schristos     ctf_dprintf ("%s: %s\n", is_warning ? _("error") : _("warning"),
2398dffb485Schristos 		 cew->cew_text);
2408dffb485Schristos 
2418dffb485Schristos   if (fp != NULL)
2428dffb485Schristos     ctf_list_append (&fp->ctf_errs_warnings, cew);
2438dffb485Schristos   else
2448dffb485Schristos     ctf_list_append (&open_errors, cew);
2458dffb485Schristos }
2468dffb485Schristos 
2478dffb485Schristos /* Move all the errors/warnings from an fp into the open_errors.  */
2488dffb485Schristos void
2494b169a6bSchristos ctf_err_warn_to_open (ctf_dict_t *fp)
2508dffb485Schristos {
2518dffb485Schristos   ctf_list_splice (&open_errors, &fp->ctf_errs_warnings);
2528dffb485Schristos }
2538dffb485Schristos 
2548dffb485Schristos /* Error-warning reporting: an 'iterator' that returns errors and warnings from
2558dffb485Schristos    the error/warning list, in order of emission.  Errors and warnings are popped
2568dffb485Schristos    after return: the caller must free the returned error-text pointer.
2578dffb485Schristos 
2588dffb485Schristos    An fp of NULL returns CTF-open-time errors from the open_errors variable
2598dffb485Schristos    above.
2608dffb485Schristos 
2618dffb485Schristos    The treatment of errors from this function itself is somewhat unusual: it
2628dffb485Schristos    will often be called on an error path, so we don't want to overwrite the
2638dffb485Schristos    ctf_errno unless we have no choice.  So, like ctf_bufopen et al, this
2648dffb485Schristos    function takes an errp pointer where errors are reported.  The pointer is
2658dffb485Schristos    optional: if not set, errors are reported via the fp (if non-NULL).  Calls
2668dffb485Schristos    with neither fp nor errp set are mildly problematic because there is no clear
2678dffb485Schristos    way to report end-of-iteration: you just have to assume that a NULL return
2688dffb485Schristos    means the end, and not an iterator error.  */
2698dffb485Schristos 
2708dffb485Schristos char *
2714b169a6bSchristos ctf_errwarning_next (ctf_dict_t *fp, ctf_next_t **it, int *is_warning,
2728dffb485Schristos 		     int *errp)
2738dffb485Schristos {
2748dffb485Schristos   ctf_next_t *i = *it;
2758dffb485Schristos   char *ret;
2768dffb485Schristos   ctf_list_t *errlist;
2778dffb485Schristos   ctf_err_warning_t *cew;
2788dffb485Schristos 
2798dffb485Schristos   if (fp)
2808dffb485Schristos     errlist = &fp->ctf_errs_warnings;
2818dffb485Schristos   else
2828dffb485Schristos     errlist = &open_errors;
2838dffb485Schristos 
2848dffb485Schristos   if (!i)
2858dffb485Schristos     {
2868dffb485Schristos       if ((i = ctf_next_create ()) == NULL)
2878dffb485Schristos 	{
2888dffb485Schristos 	  if (errp)
2898dffb485Schristos 	    *errp = ENOMEM;
2908dffb485Schristos 	  else if (fp)
2918dffb485Schristos 	    ctf_set_errno (fp, ENOMEM);
2928dffb485Schristos 	  return NULL;
2938dffb485Schristos 	}
2948dffb485Schristos 
2958dffb485Schristos       i->cu.ctn_fp = fp;
2968dffb485Schristos       i->ctn_iter_fun = (void (*) (void)) ctf_errwarning_next;
2978dffb485Schristos       *it = i;
2988dffb485Schristos     }
2998dffb485Schristos 
3008dffb485Schristos   if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun)
3018dffb485Schristos     {
3028dffb485Schristos       if (errp)
3038dffb485Schristos 	*errp = ECTF_NEXT_WRONGFUN;
3048dffb485Schristos       else if (fp)
3058dffb485Schristos 	ctf_set_errno (fp, ECTF_NEXT_WRONGFUN);
3068dffb485Schristos       return NULL;
3078dffb485Schristos     }
3088dffb485Schristos 
3098dffb485Schristos   if (fp != i->cu.ctn_fp)
3108dffb485Schristos     {
3118dffb485Schristos       if (errp)
3128dffb485Schristos 	*errp = ECTF_NEXT_WRONGFP;
3138dffb485Schristos       else if (fp)
3148dffb485Schristos 	ctf_set_errno (fp, ECTF_NEXT_WRONGFP);
3158dffb485Schristos       return NULL;
3168dffb485Schristos     }
3178dffb485Schristos 
3188dffb485Schristos   cew = ctf_list_next (errlist);
3198dffb485Schristos 
3208dffb485Schristos   if (!cew)
3218dffb485Schristos     {
3228dffb485Schristos       ctf_next_destroy (i);
3238dffb485Schristos       *it = NULL;
3248dffb485Schristos       if (errp)
3258dffb485Schristos 	*errp = ECTF_NEXT_END;
3268dffb485Schristos       else if (fp)
3278dffb485Schristos 	ctf_set_errno (fp, ECTF_NEXT_END);
3288dffb485Schristos       return NULL;
3298dffb485Schristos     }
3308dffb485Schristos 
3318dffb485Schristos   if (is_warning)
3328dffb485Schristos     *is_warning = cew->cew_is_warning;
3338dffb485Schristos   ret = cew->cew_text;
3348dffb485Schristos   ctf_list_delete (errlist, cew);
3358dffb485Schristos   free (cew);
3368dffb485Schristos   return ret;
3378dffb485Schristos }
3388dffb485Schristos 
3398dffb485Schristos void
3404b169a6bSchristos ctf_assert_fail_internal (ctf_dict_t *fp, const char *file, size_t line,
3418dffb485Schristos 			  const char *exprstr)
3428dffb485Schristos {
3438dffb485Schristos   ctf_set_errno (fp, ECTF_INTERNAL);
344*c9055873Schristos   ctf_err_warn (fp, 0, 0, _("%s: %lu: libctf assertion failed: %s"),
345*c9055873Schristos 		file, (long unsigned int) line, exprstr);
3468dffb485Schristos }
347