text: /* ld.so error exception allocation and deallocation.
     Copyright (C) 1995-2020 Free Software Foundation, Inc.
     This file is part of the GNU C Library.
  
     The GNU C Library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
     License as published by the Free Software Foundation; either
     version 2.1 of the License, or (at your option) any later version.
  
     The GNU C Library is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Lesser General Public License for more details.
  
     You should have received a copy of the GNU Lesser General Public
     License along with the GNU C Library; if not, see
     <https://www.gnu.org/licenses/>.  */
  
  #include <ldsodefs.h>
  #include <limits.h>
  #include <stdarg.h>
  #include <stdbool.h>
  #include <stdint.h>
  #include <string.h>
  #include <unistd.h>
  #include <_itoa.h>
  
  /* This message we return as a last resort.  We define the string in a
     variable since we have to avoid freeing it and so have to enable
     a pointer comparison.  See below and in dlfcn/dlerror.c.  */
  static const char _dl_out_of_memory[] = "out of memory";
  
  /* Dummy allocation object used if allocating the message buffer
     fails.  */
  static void
  oom_exception (struct dl_exception *exception)
  {
    exception->objname = "";
    exception->errstring = _dl_out_of_memory;
    exception->message_buffer = NULL;
  }
  
  static void
  __attribute__ ((noreturn))
  length_mismatch (void)
  {
    _dl_fatal_printf ("Fatal error: "
                      "length accounting in _dl_exception_create_format\n");
  }
  
  /* Adjust the message buffer to indicate whether it is possible to
     free it.  EXCEPTION->errstring must be a potentially deallocatable
     pointer.  */
  static void
  adjust_message_buffer (struct dl_exception *exception)
  {
    /* If the main executable is relocated it means the libc's malloc
       is used.  */
    bool malloced = true;
  #ifdef SHARED
    malloced = (GL(dl_ns)[LM_ID_BASE]._ns_loaded != NULL
                && (GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_relocated != 0));
  #endif
    if (malloced)
      exception->message_buffer = (char *) exception->errstring;
    else
      exception->message_buffer = NULL;
  }
  
  void
  _dl_exception_create (struct dl_exception *exception, const char *objname,
                        const char *errstring)
  {
    if (objname == NULL)
      objname = "";
    size_t len_objname = strlen (objname) + 1;
    size_t len_errstring = strlen (errstring) + 1;
    char *errstring_copy = malloc (len_objname + len_errstring);
    if (errstring_copy != NULL)
      {
        /* Make a copy of the object file name and the error string.  */
        exception->objname = memcpy (__mempcpy (errstring_copy,
                                                errstring, len_errstring),
                                     objname, len_objname);
        exception->errstring = errstring_copy;
        adjust_message_buffer (exception);
      }
    else
      oom_exception (exception);
  }
  rtld_hidden_def (_dl_exception_create)
  
  void
  _dl_exception_create_format (struct dl_exception *exception, const char *objname,
                               const char *fmt, ...)
  {
    if (objname == NULL)
      objname = "";
    size_t len_objname = strlen (objname) + 1;
    /* Compute the length of the result.  Include room for two NUL
       bytes.  */
    size_t length = len_objname + 1;
    {
      va_list ap;
      va_start (ap, fmt);
      for (const char *p = fmt; *p != '\0'; ++p)
        if (*p == '%')
          {
            ++p;
            switch (*p)
              {
              case 's':
                length += strlen (va_arg (ap, const char *));
                break;
  	      /* Recognize the l modifier.  It is only important on some
  		 platforms where long and int have a different size.  We
  		 can use the same code for size_t.  */
  	    case 'l':
  	    case 'z':
  	      if (p[1] == 'x')
  		{
  		  length += LONG_WIDTH / 4;
  		  ++p;
  		  break;
  		}
  	      /* Fall through.  */
  	    case 'x':
  	      length += INT_WIDTH / 4;
  	      break;
              default:
                /* Assumed to be '%'.  */
                ++length;
                break;
              }
          }
        else
          ++length;
      va_end (ap);
    }
  
    if (length > PTRDIFF_MAX)
      {
        oom_exception (exception);
        return;
      }
    char *errstring = malloc (length);
    if (errstring == NULL)
      {
        oom_exception (exception);
        return;
      }
    exception->errstring = errstring;
    adjust_message_buffer (exception);
  
    /* Copy the error message to errstring.  */
    {
      /* Next byte to be written in errstring.  */
      char *wptr = errstring;
      /* End of the allocated string.  */
      char *const end = errstring + length;
  
      va_list ap;
      va_start (ap, fmt);
  
      for (const char *p = fmt; *p != '\0'; ++p)
        if (*p == '%')
          {
            ++p;
            switch (*p)
              {
              case 's':
                {
                  const char *ptr = va_arg (ap, const char *);
                  size_t len_ptr = strlen (ptr);
                  if (len_ptr > end - wptr)
                    length_mismatch ();
                  wptr = __mempcpy (wptr, ptr, len_ptr);
                }
                break;
              case '%':
                if (wptr == end)
                  length_mismatch ();
                *wptr = '%';
                ++wptr;
                break;
  	    case 'x':
  	      {
  		unsigned long int num = va_arg (ap, unsigned int);
  		char *start = wptr;
  		wptr += INT_WIDTH / 4;
  		char *cp = _itoa (num, wptr, 16, 0);
  		/* Pad to the full width with 0.  */
  		while (cp != start)
  		  *--cp = '0';
  	      }
  	      break;
  	    case 'l':
  	    case 'z':
  	      if (p[1] == 'x')
  		{
  		  unsigned long int num = va_arg (ap, unsigned long int);
  		  char *start = wptr;
  		  wptr += LONG_WIDTH / 4;
  		  char *cp = _itoa (num, wptr, 16, 0);
  		  /* Pad to the full width with 0.  */
  		  while (cp != start)
  		    *--cp = '0';
  		  ++p;
  		  break;
  		}
  	       /* FALLTHROUGH */
              default:
                _dl_fatal_printf ("Fatal error:"
                                  " invalid format in exception string\n");
              }
          }
        else
          {
            if (wptr == end)
              length_mismatch ();
            *wptr = *p;
            ++wptr;
          }
  
      if (wptr == end)
        length_mismatch ();
      *wptr = '\0';
      ++wptr;
      if (len_objname != end - wptr)
        length_mismatch ();
      exception->objname = memcpy (wptr, objname, len_objname);
    }
  }
  rtld_hidden_def (_dl_exception_create_format)
  
  void
  _dl_exception_free (struct dl_exception *exception)
  {
    free (exception->message_buffer);
    exception->objname = NULL;
    exception->errstring = NULL;
    exception->message_buffer = NULL;
  }
  rtld_hidden_def (_dl_exception_free)
  