xref: /netbsd-src/external/gpl3/binutils/dist/libctf/ctf-subr.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
16f4ced0bSchristos /* Simple subrs.
2*cb63e24eSchristos    Copyright (C) 2019-2024 Free Software Foundation, Inc.
36f4ced0bSchristos 
46f4ced0bSchristos    This file is part of libctf.
56f4ced0bSchristos 
66f4ced0bSchristos    libctf is free software; you can redistribute it and/or modify it under
76f4ced0bSchristos    the terms of the GNU General Public License as published by the Free
86f4ced0bSchristos    Software Foundation; either version 3, or (at your option) any later
96f4ced0bSchristos    version.
106f4ced0bSchristos 
116f4ced0bSchristos    This program is distributed in the hope that it will be useful, but
126f4ced0bSchristos    WITHOUT ANY WARRANTY; without even the implied warranty of
136f4ced0bSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
146f4ced0bSchristos    See the GNU General Public License for more details.
156f4ced0bSchristos 
166f4ced0bSchristos    You should have received a copy of the GNU General Public License
176f4ced0bSchristos    along with this program; see the file COPYING.  If not see
186f4ced0bSchristos    <http://www.gnu.org/licenses/>.  */
196f4ced0bSchristos 
206f4ced0bSchristos #include <ctf-impl.h>
216f4ced0bSchristos #ifdef HAVE_MMAP
226f4ced0bSchristos #include <sys/mman.h>
236f4ced0bSchristos #endif
246f4ced0bSchristos #include <sys/types.h>
256f4ced0bSchristos #include <stdarg.h>
266f4ced0bSchristos #include <string.h>
276f4ced0bSchristos #include <unistd.h>
286f4ced0bSchristos 
294f645668Schristos #ifndef ENOTSUP
304f645668Schristos #define ENOTSUP ENOSYS
314f645668Schristos #endif
324f645668Schristos 
336f4ced0bSchristos int _libctf_version = CTF_VERSION;	      /* Library client version.  */
346f4ced0bSchristos int _libctf_debug = 0;			      /* Debugging messages enabled.  */
356f4ced0bSchristos 
366f4ced0bSchristos /* Private, read-only mmap from a file, with fallback to copying.
376f4ced0bSchristos 
386f4ced0bSchristos    No handling of page-offset issues at all: the caller must allow for that. */
396f4ced0bSchristos 
406f4ced0bSchristos _libctf_malloc_ void *
ctf_mmap(size_t length,size_t offset,int fd)416f4ced0bSchristos ctf_mmap (size_t length, size_t offset, int fd)
426f4ced0bSchristos {
436f4ced0bSchristos   void *data;
446f4ced0bSchristos 
456f4ced0bSchristos #ifdef HAVE_MMAP
466f4ced0bSchristos   data = mmap (NULL, length, PROT_READ, MAP_PRIVATE, fd, offset);
476f4ced0bSchristos   if (data == MAP_FAILED)
486f4ced0bSchristos     data = NULL;
496f4ced0bSchristos #else
506f4ced0bSchristos   if ((data = malloc (length)) != NULL)
516f4ced0bSchristos     {
526f4ced0bSchristos       if (ctf_pread (fd, data, length, offset) <= 0)
536f4ced0bSchristos 	{
546f4ced0bSchristos 	  free (data);
556f4ced0bSchristos 	  data = NULL;
566f4ced0bSchristos 	}
576f4ced0bSchristos     }
586f4ced0bSchristos #endif
596f4ced0bSchristos   return data;
606f4ced0bSchristos }
616f4ced0bSchristos 
626f4ced0bSchristos void
ctf_munmap(void * buf,size_t length _libctf_unused_)636f4ced0bSchristos ctf_munmap (void *buf, size_t length _libctf_unused_)
646f4ced0bSchristos {
656f4ced0bSchristos #ifdef HAVE_MMAP
666f4ced0bSchristos   (void) munmap (buf, length);
676f4ced0bSchristos #else
686f4ced0bSchristos   free (buf);
696f4ced0bSchristos #endif
706f4ced0bSchristos }
716f4ced0bSchristos 
726f4ced0bSchristos ssize_t
ctf_pread(int fd,void * buf,ssize_t count,off_t offset)736f4ced0bSchristos ctf_pread (int fd, void *buf, ssize_t count, off_t offset)
746f4ced0bSchristos {
756f4ced0bSchristos   ssize_t len;
766f4ced0bSchristos   size_t acc = 0;
776f4ced0bSchristos   char *data = (char *) buf;
786f4ced0bSchristos 
796f4ced0bSchristos #ifdef HAVE_PREAD
806f4ced0bSchristos   while (count > 0)
816f4ced0bSchristos     {
826f4ced0bSchristos       errno = 0;
836f4ced0bSchristos       if (((len = pread (fd, data, count, offset)) < 0) &&
846f4ced0bSchristos 	  errno != EINTR)
856f4ced0bSchristos 	  return len;
866f4ced0bSchristos       if (errno == EINTR)
876f4ced0bSchristos 	continue;
886f4ced0bSchristos 
896f4ced0bSchristos       acc += len;
906f4ced0bSchristos       if (len == 0)				/* EOF.  */
916f4ced0bSchristos 	return acc;
926f4ced0bSchristos 
936f4ced0bSchristos       count -= len;
946f4ced0bSchristos       offset += len;
956f4ced0bSchristos       data += len;
966f4ced0bSchristos     }
976f4ced0bSchristos   return acc;
986f4ced0bSchristos #else
996f4ced0bSchristos   off_t orig_off;
1006f4ced0bSchristos 
1016f4ced0bSchristos   if ((orig_off = lseek (fd, 0, SEEK_CUR)) < 0)
1026f4ced0bSchristos     return -1;
1036f4ced0bSchristos   if ((lseek (fd, offset, SEEK_SET)) < 0)
1046f4ced0bSchristos     return -1;
1056f4ced0bSchristos 
1066f4ced0bSchristos   while (count > 0)
1076f4ced0bSchristos     {
1086f4ced0bSchristos       errno = 0;
1096f4ced0bSchristos       if (((len = read (fd, data, count)) < 0) &&
1106f4ced0bSchristos 	  errno != EINTR)
1116f4ced0bSchristos 	  return len;
1126f4ced0bSchristos       if (errno == EINTR)
1136f4ced0bSchristos 	continue;
1146f4ced0bSchristos 
1156f4ced0bSchristos       acc += len;
1166f4ced0bSchristos       if (len == 0)				/* EOF.  */
1176f4ced0bSchristos 	break;
1186f4ced0bSchristos 
1196f4ced0bSchristos       count -= len;
1206f4ced0bSchristos       data += len;
1216f4ced0bSchristos     }
1226f4ced0bSchristos   if ((lseek (fd, orig_off, SEEK_SET)) < 0)
1236f4ced0bSchristos     return -1;					/* offset is smashed.  */
1246f4ced0bSchristos #endif
1256f4ced0bSchristos 
1266f4ced0bSchristos   return acc;
1276f4ced0bSchristos }
1286f4ced0bSchristos 
1296f4ced0bSchristos /* Set the CTF library client version to the specified version.  If version is
1306f4ced0bSchristos    zero, we just return the default library version number.  */
1316f4ced0bSchristos int
ctf_version(int version)1326f4ced0bSchristos ctf_version (int version)
1336f4ced0bSchristos {
1346f4ced0bSchristos   if (version < 0)
1356f4ced0bSchristos     {
1366f4ced0bSchristos       errno = EINVAL;
1376f4ced0bSchristos       return -1;
1386f4ced0bSchristos     }
1396f4ced0bSchristos 
1406f4ced0bSchristos   if (version > 0)
1416f4ced0bSchristos     {
1426f4ced0bSchristos       /*  Dynamic version switching is not presently supported. */
1436f4ced0bSchristos       if (version != CTF_VERSION)
1446f4ced0bSchristos 	{
1456f4ced0bSchristos 	  errno = ENOTSUP;
1466f4ced0bSchristos 	  return -1;
1476f4ced0bSchristos 	}
1486f4ced0bSchristos       ctf_dprintf ("ctf_version: client using version %d\n", version);
1496f4ced0bSchristos       _libctf_version = version;
1506f4ced0bSchristos     }
1516f4ced0bSchristos 
1526f4ced0bSchristos   return _libctf_version;
1536f4ced0bSchristos }
1546f4ced0bSchristos 
1556f4ced0bSchristos void
libctf_init_debug(void)1566f4ced0bSchristos libctf_init_debug (void)
1576f4ced0bSchristos {
1586f4ced0bSchristos   static int inited;
1596f4ced0bSchristos   if (!inited)
1606f4ced0bSchristos     {
1616f4ced0bSchristos       _libctf_debug = getenv ("LIBCTF_DEBUG") != NULL;
1626f4ced0bSchristos       inited = 1;
1636f4ced0bSchristos     }
1646f4ced0bSchristos }
1656f4ced0bSchristos 
ctf_setdebug(int debug)1666f4ced0bSchristos void ctf_setdebug (int debug)
1676f4ced0bSchristos {
1686f4ced0bSchristos   /* Ensure that libctf_init_debug() has been called, so that we don't get our
1696f4ced0bSchristos      debugging-on-or-off smashed by the next call.  */
1706f4ced0bSchristos 
1716f4ced0bSchristos   libctf_init_debug();
1726f4ced0bSchristos   _libctf_debug = debug;
1736f4ced0bSchristos   ctf_dprintf ("CTF debugging set to %i\n", debug);
1746f4ced0bSchristos }
1756f4ced0bSchristos 
ctf_getdebug(void)1766f4ced0bSchristos int ctf_getdebug (void)
1776f4ced0bSchristos {
1786f4ced0bSchristos   return _libctf_debug;
1796f4ced0bSchristos }
1806f4ced0bSchristos 
1816f4ced0bSchristos _libctf_printflike_ (1, 2)
ctf_dprintf(const char * format,...)1826f4ced0bSchristos void ctf_dprintf (const char *format, ...)
1836f4ced0bSchristos {
1844f645668Schristos   if (_libctf_unlikely_ (_libctf_debug))
1856f4ced0bSchristos     {
1866f4ced0bSchristos       va_list alist;
1876f4ced0bSchristos 
1886f4ced0bSchristos       va_start (alist, format);
1896f4ced0bSchristos       fflush (stdout);
1906f4ced0bSchristos       (void) fputs ("libctf DEBUG: ", stderr);
1916f4ced0bSchristos       (void) vfprintf (stderr, format, alist);
1926f4ced0bSchristos       va_end (alist);
1936f4ced0bSchristos     }
1946f4ced0bSchristos }
1954f645668Schristos 
1964f645668Schristos /* This needs more attention to thread-safety later on.  */
1974f645668Schristos static ctf_list_t open_errors;
1984f645668Schristos 
1994f645668Schristos /* Errors and warnings.  Report the warning or error to the list in FP (or the
2004f645668Schristos    open errors list if NULL): if ERR is nonzero it is the errno to report to the
2014f645668Schristos    debug stream instead of that recorded on fp.  */
2024f645668Schristos _libctf_printflike_ (4, 5)
2034f645668Schristos extern void
ctf_err_warn(ctf_dict_t * fp,int is_warning,int err,const char * format,...)2044f645668Schristos ctf_err_warn (ctf_dict_t *fp, int is_warning, int err,
2054f645668Schristos 	      const char *format, ...)
2064f645668Schristos {
2074f645668Schristos   va_list alist;
2084f645668Schristos   ctf_err_warning_t *cew;
2094f645668Schristos 
2104f645668Schristos   /* Don't bother reporting errors here: we can't do much about them if they
2114f645668Schristos      happen.  If we're so short of memory that a tiny malloc doesn't work, a
2124f645668Schristos      vfprintf isn't going to work either and the caller will have to rely on the
2134f645668Schristos      ENOMEM return they'll be getting in short order anyway.  */
2144f645668Schristos 
2154f645668Schristos   if ((cew = malloc (sizeof (ctf_err_warning_t))) == NULL)
2164f645668Schristos     return;
2174f645668Schristos 
2184f645668Schristos   cew->cew_is_warning = is_warning;
2194f645668Schristos   va_start (alist, format);
2204f645668Schristos   if (vasprintf (&cew->cew_text, format, alist) < 0)
2214f645668Schristos     {
2224f645668Schristos       free (cew);
2234f645668Schristos       va_end (alist);
2244f645668Schristos       return;
2254f645668Schristos     }
2264f645668Schristos   va_end (alist);
2274f645668Schristos 
2284f645668Schristos   /* Include the error code only if there is one; if this is not a warning,
2294f645668Schristos      only use the error code if it was explicitly passed and is nonzero.
2304f645668Schristos      (Warnings may not have a meaningful error code, since the warning may not
2314f645668Schristos      lead to unwinding up to the user.)  */
2324f645668Schristos   if ((!is_warning && (err != 0 || (fp && ctf_errno (fp) != 0)))
2334f645668Schristos       || (is_warning && err != 0))
2344f645668Schristos     ctf_dprintf ("%s: %s (%s)\n", is_warning ? _("error") : _("warning"),
2354f645668Schristos 		 cew->cew_text, err != 0 ? ctf_errmsg (err)
2364f645668Schristos 		 : ctf_errmsg (ctf_errno (fp)));
2374f645668Schristos   else
2384f645668Schristos     ctf_dprintf ("%s: %s\n", is_warning ? _("error") : _("warning"),
2394f645668Schristos 		 cew->cew_text);
2404f645668Schristos 
2414f645668Schristos   if (fp != NULL)
2424f645668Schristos     ctf_list_append (&fp->ctf_errs_warnings, cew);
2434f645668Schristos   else
2444f645668Schristos     ctf_list_append (&open_errors, cew);
2454f645668Schristos }
2464f645668Schristos 
2474f645668Schristos /* Move all the errors/warnings from an fp into the open_errors.  */
2484f645668Schristos void
ctf_err_warn_to_open(ctf_dict_t * fp)2494f645668Schristos ctf_err_warn_to_open (ctf_dict_t *fp)
2504f645668Schristos {
2514f645668Schristos   ctf_list_splice (&open_errors, &fp->ctf_errs_warnings);
2524f645668Schristos }
2534f645668Schristos 
2544f645668Schristos /* Error-warning reporting: an 'iterator' that returns errors and warnings from
2554f645668Schristos    the error/warning list, in order of emission.  Errors and warnings are popped
2564f645668Schristos    after return: the caller must free the returned error-text pointer.
2574f645668Schristos 
2584f645668Schristos    An fp of NULL returns CTF-open-time errors from the open_errors variable
2594f645668Schristos    above.
2604f645668Schristos 
2614f645668Schristos    The treatment of errors from this function itself is somewhat unusual: it
2624f645668Schristos    will often be called on an error path, so we don't want to overwrite the
2634f645668Schristos    ctf_errno unless we have no choice.  So, like ctf_bufopen et al, this
2644f645668Schristos    function takes an errp pointer where errors are reported.  The pointer is
2654f645668Schristos    optional: if not set, errors are reported via the fp (if non-NULL).  Calls
2664f645668Schristos    with neither fp nor errp set are mildly problematic because there is no clear
2674f645668Schristos    way to report end-of-iteration: you just have to assume that a NULL return
2684f645668Schristos    means the end, and not an iterator error.  */
2694f645668Schristos 
2704f645668Schristos char *
ctf_errwarning_next(ctf_dict_t * fp,ctf_next_t ** it,int * is_warning,int * errp)2714f645668Schristos ctf_errwarning_next (ctf_dict_t *fp, ctf_next_t **it, int *is_warning,
2724f645668Schristos 		     int *errp)
2734f645668Schristos {
2744f645668Schristos   ctf_next_t *i = *it;
2754f645668Schristos   char *ret;
2764f645668Schristos   ctf_list_t *errlist;
2774f645668Schristos   ctf_err_warning_t *cew;
2784f645668Schristos 
2794f645668Schristos   if (fp)
2804f645668Schristos     errlist = &fp->ctf_errs_warnings;
2814f645668Schristos   else
2824f645668Schristos     errlist = &open_errors;
2834f645668Schristos 
2844f645668Schristos   if (!i)
2854f645668Schristos     {
2864f645668Schristos       if ((i = ctf_next_create ()) == NULL)
2874f645668Schristos 	{
2884f645668Schristos 	  if (errp)
2894f645668Schristos 	    *errp = ENOMEM;
2904f645668Schristos 	  else if (fp)
2914f645668Schristos 	    ctf_set_errno (fp, ENOMEM);
2924f645668Schristos 	  return NULL;
2934f645668Schristos 	}
2944f645668Schristos 
2954f645668Schristos       i->cu.ctn_fp = fp;
2964f645668Schristos       i->ctn_iter_fun = (void (*) (void)) ctf_errwarning_next;
2974f645668Schristos       *it = i;
2984f645668Schristos     }
2994f645668Schristos 
3004f645668Schristos   if ((void (*) (void)) ctf_errwarning_next != i->ctn_iter_fun)
3014f645668Schristos     {
3024f645668Schristos       if (errp)
3034f645668Schristos 	*errp = ECTF_NEXT_WRONGFUN;
3044f645668Schristos       else if (fp)
3054f645668Schristos 	ctf_set_errno (fp, ECTF_NEXT_WRONGFUN);
3064f645668Schristos       return NULL;
3074f645668Schristos     }
3084f645668Schristos 
3094f645668Schristos   if (fp != i->cu.ctn_fp)
3104f645668Schristos     {
3114f645668Schristos       if (errp)
3124f645668Schristos 	*errp = ECTF_NEXT_WRONGFP;
3134f645668Schristos       else if (fp)
3144f645668Schristos 	ctf_set_errno (fp, ECTF_NEXT_WRONGFP);
3154f645668Schristos       return NULL;
3164f645668Schristos     }
3174f645668Schristos 
3184f645668Schristos   cew = ctf_list_next (errlist);
3194f645668Schristos 
3204f645668Schristos   if (!cew)
3214f645668Schristos     {
3224f645668Schristos       ctf_next_destroy (i);
3234f645668Schristos       *it = NULL;
3244f645668Schristos       if (errp)
3254f645668Schristos 	*errp = ECTF_NEXT_END;
3264f645668Schristos       else if (fp)
3274f645668Schristos 	ctf_set_errno (fp, ECTF_NEXT_END);
3284f645668Schristos       return NULL;
3294f645668Schristos     }
3304f645668Schristos 
3314f645668Schristos   if (is_warning)
3324f645668Schristos     *is_warning = cew->cew_is_warning;
3334f645668Schristos   ret = cew->cew_text;
3344f645668Schristos   ctf_list_delete (errlist, cew);
3354f645668Schristos   free (cew);
3364f645668Schristos   return ret;
3374f645668Schristos }
3384f645668Schristos 
3394f645668Schristos void
ctf_assert_fail_internal(ctf_dict_t * fp,const char * file,size_t line,const char * exprstr)3404f645668Schristos ctf_assert_fail_internal (ctf_dict_t *fp, const char *file, size_t line,
3414f645668Schristos 			  const char *exprstr)
3424f645668Schristos {
3434f645668Schristos   ctf_err_warn (fp, 0, ECTF_INTERNAL, _("%s: %lu: libctf assertion failed: %s"),
3444f645668Schristos 		file, (long unsigned int) line, exprstr);
3454f645668Schristos   ctf_set_errno (fp, ECTF_INTERNAL);
3464f645668Schristos }
347