Blame elf/dl-cache.c

Packit Service 82fcde
/* Support for reading /etc/ld.so.cache files written by Linux ldconfig.
Packit Service 82fcde
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <ldsodefs.h>
Packit Service 82fcde
#include <sys/mman.h>
Packit Service 82fcde
#include <dl-cache.h>
Packit Service 82fcde
#include <dl-procinfo.h>
Packit Service 82fcde
#include <stdint.h>
Packit Service 82fcde
#include <_itoa.h>
Packit Service 82fcde
#include <dl-hwcaps.h>
Packit Service 82fcde
Packit Service 82fcde
#ifndef _DL_PLATFORMS_COUNT
Packit Service 82fcde
# define _DL_PLATFORMS_COUNT 0
Packit Service 82fcde
#endif
Packit Service 82fcde
Packit Service 82fcde
/* This is the starting address and the size of the mmap()ed file.  */
Packit Service 82fcde
static struct cache_file *cache;
Packit Service 82fcde
static struct cache_file_new *cache_new;
Packit Service 82fcde
static size_t cachesize;
Packit Service 82fcde
Packit Service 82fcde
/* 1 if cache_data + PTR points into the cache.  */
Packit Service 82fcde
#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
Packit Service 82fcde
Packit Service 82fcde
#define SEARCH_CACHE(cache) \
Packit Service 82fcde
/* We use binary search since the table is sorted in the cache file.	      \
Packit Service 82fcde
   The first matching entry in the table is returned.			      \
Packit Service 82fcde
   It is important to use the same algorithm as used while generating	      \
Packit Service 82fcde
   the cache file.  */							      \
Packit Service 82fcde
do									      \
Packit Service 82fcde
  {									      \
Packit Service 82fcde
    left = 0;								      \
Packit Service 82fcde
    right = cache->nlibs - 1;						      \
Packit Service 82fcde
									      \
Packit Service 82fcde
    while (left <= right)						      \
Packit Service 82fcde
      {									      \
Packit Service 82fcde
	__typeof__ (cache->libs[0].key) key;				      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	middle = (left + right) / 2;					      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	key = cache->libs[middle].key;					      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	/* Make sure string table indices are not bogus before using	      \
Packit Service 82fcde
	   them.  */							      \
Packit Service 82fcde
	if (! _dl_cache_verify_ptr (key))				      \
Packit Service 82fcde
	  {								      \
Packit Service 82fcde
	    cmpres = 1;							      \
Packit Service 82fcde
	    break;							      \
Packit Service 82fcde
	  }								      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	/* Actually compare the entry with the key.  */			      \
Packit Service 82fcde
	cmpres = _dl_cache_libcmp (name, cache_data + key);		      \
Packit Service 82fcde
	if (__glibc_unlikely (cmpres == 0))				      \
Packit Service 82fcde
	  {								      \
Packit Service 82fcde
	    /* Found it.  LEFT now marks the last entry for which we	      \
Packit Service 82fcde
	       know the name is correct.  */				      \
Packit Service 82fcde
	    left = middle;						      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	    /* There might be entries with this name before the one we	      \
Packit Service 82fcde
	       found.  So we have to find the beginning.  */		      \
Packit Service 82fcde
	    while (middle > 0)						      \
Packit Service 82fcde
	      {								      \
Packit Service 82fcde
		__typeof__ (cache->libs[0].key) key;			      \
Packit Service 82fcde
									      \
Packit Service 82fcde
		key = cache->libs[middle - 1].key;			      \
Packit Service 82fcde
		/* Make sure string table indices are not bogus before	      \
Packit Service 82fcde
		   using them.  */					      \
Packit Service 82fcde
		if (! _dl_cache_verify_ptr (key)			      \
Packit Service 82fcde
		    /* Actually compare the entry.  */			      \
Packit Service 82fcde
		    || _dl_cache_libcmp (name, cache_data + key) != 0)	      \
Packit Service 82fcde
		  break;						      \
Packit Service 82fcde
		--middle;						      \
Packit Service 82fcde
	      }								      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	    do								      \
Packit Service 82fcde
	      {								      \
Packit Service 82fcde
		int flags;						      \
Packit Service 82fcde
		__typeof__ (cache->libs[0]) *lib = &cache->libs[middle];      \
Packit Service 82fcde
									      \
Packit Service 82fcde
		/* Only perform the name test if necessary.  */		      \
Packit Service 82fcde
		if (middle > left					      \
Packit Service 82fcde
		    /* We haven't seen this string so far.  Test whether the  \
Packit Service 82fcde
		       index is ok and whether the name matches.  Otherwise   \
Packit Service 82fcde
		       we are done.  */					      \
Packit Service 82fcde
		    && (! _dl_cache_verify_ptr (lib->key)		      \
Packit Service 82fcde
			|| (_dl_cache_libcmp (name, cache_data + lib->key)    \
Packit Service 82fcde
			    != 0)))					      \
Packit Service 82fcde
		  break;						      \
Packit Service 82fcde
									      \
Packit Service 82fcde
		flags = lib->flags;					      \
Packit Service 82fcde
		if (_dl_cache_check_flags (flags)			      \
Packit Service 82fcde
		    && _dl_cache_verify_ptr (lib->value))		      \
Packit Service 82fcde
		  {							      \
Packit Service 82fcde
		    if (best == NULL || flags == GLRO(dl_correct_cache_id))   \
Packit Service 82fcde
		      {							      \
Packit Service 82fcde
			HWCAP_CHECK;					      \
Packit Service 82fcde
			best = cache_data + lib->value;			      \
Packit Service 82fcde
									      \
Packit Service 82fcde
			if (flags == GLRO(dl_correct_cache_id))		      \
Packit Service 82fcde
			  /* We've found an exact match for the shared	      \
Packit Service 82fcde
			     object and no general `ELF' release.  Stop	      \
Packit Service 82fcde
			     searching.  */				      \
Packit Service 82fcde
			  break;					      \
Packit Service 82fcde
		      }							      \
Packit Service 82fcde
		  }							      \
Packit Service 82fcde
	      }								      \
Packit Service 82fcde
	    while (++middle <= right);					      \
Packit Service 82fcde
	    break;							      \
Packit Service 82fcde
	}								      \
Packit Service 82fcde
									      \
Packit Service 82fcde
	if (cmpres < 0)							      \
Packit Service 82fcde
	  left = middle + 1;						      \
Packit Service 82fcde
	else								      \
Packit Service 82fcde
	  right = middle - 1;						      \
Packit Service 82fcde
      }									      \
Packit Service 82fcde
  }									      \
Packit Service 82fcde
while (0)
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
_dl_cache_libcmp (const char *p1, const char *p2)
Packit Service 82fcde
{
Packit Service 82fcde
  while (*p1 != '\0')
Packit Service 82fcde
    {
Packit Service 82fcde
      if (*p1 >= '0' && *p1 <= '9')
Packit Service 82fcde
        {
Packit Service 82fcde
          if (*p2 >= '0' && *p2 <= '9')
Packit Service 82fcde
            {
Packit Service 82fcde
	      /* Must compare this numerically.  */
Packit Service 82fcde
	      int val1;
Packit Service 82fcde
	      int val2;
Packit Service 82fcde
Packit Service 82fcde
	      val1 = *p1++ - '0';
Packit Service 82fcde
	      val2 = *p2++ - '0';
Packit Service 82fcde
	      while (*p1 >= '0' && *p1 <= '9')
Packit Service 82fcde
	        val1 = val1 * 10 + *p1++ - '0';
Packit Service 82fcde
	      while (*p2 >= '0' && *p2 <= '9')
Packit Service 82fcde
	        val2 = val2 * 10 + *p2++ - '0';
Packit Service 82fcde
	      if (val1 != val2)
Packit Service 82fcde
		return val1 - val2;
Packit Service 82fcde
	    }
Packit Service 82fcde
	  else
Packit Service 82fcde
            return 1;
Packit Service 82fcde
        }
Packit Service 82fcde
      else if (*p2 >= '0' && *p2 <= '9')
Packit Service 82fcde
        return -1;
Packit Service 82fcde
      else if (*p1 != *p2)
Packit Service 82fcde
        return *p1 - *p2;
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  ++p1;
Packit Service 82fcde
	  ++p2;
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  return *p1 - *p2;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
/* Look up NAME in ld.so.cache and return the file name stored there, or null
Packit Service 82fcde
   if none is found.  The cache is loaded if it was not already.  If loading
Packit Service 82fcde
   the cache previously failed there will be no more attempts to load it.
Packit Service 82fcde
   The caller is responsible for freeing the returned string.  The ld.so.cache
Packit Service 82fcde
   may be unmapped at any time by a completing recursive dlopen and
Packit Service 82fcde
   this function must take care that it does not return references to
Packit Service 82fcde
   any data in the mapping.  */
Packit Service 82fcde
char *
Packit Service 82fcde
_dl_load_cache_lookup (const char *name)
Packit Service 82fcde
{
Packit Service 82fcde
  int left, right, middle;
Packit Service 82fcde
  int cmpres;
Packit Service 82fcde
  const char *cache_data;
Packit Service 82fcde
  uint32_t cache_data_size;
Packit Service 82fcde
  const char *best;
Packit Service 82fcde
Packit Service 82fcde
  /* Print a message if the loading of libs is traced.  */
Packit Service 82fcde
  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
Packit Service 82fcde
    _dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
Packit Service 82fcde
Packit Service 82fcde
  if (cache == NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Read the contents of the file.  */
Packit Service 82fcde
      void *file = _dl_sysdep_read_whole_file (LD_SO_CACHE, &cachesize,
Packit Service 82fcde
					       PROT_READ);
Packit Service 82fcde
Packit Service 82fcde
      /* We can handle three different cache file formats here:
Packit Service 82fcde
	 - the old libc5/glibc2.0/2.1 format
Packit Service 82fcde
	 - the old format with the new format in it
Packit Service 82fcde
	 - only the new format
Packit Service 82fcde
	 The following checks if the cache contains any of these formats.  */
Packit Service 82fcde
      if (file != MAP_FAILED && cachesize > sizeof *cache
Packit Service 82fcde
	  && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  size_t offset;
Packit Service 82fcde
	  /* Looks ok.  */
Packit Service 82fcde
	  cache = file;
Packit Service 82fcde
Packit Service 82fcde
	  /* Check for new version.  */
Packit Service 82fcde
	  offset = ALIGN_CACHE (sizeof (struct cache_file)
Packit Service 82fcde
				+ cache->nlibs * sizeof (struct file_entry));
Packit Service 82fcde
Packit Service 82fcde
	  cache_new = (struct cache_file_new *) ((void *) cache + offset);
Packit Service 82fcde
	  if (cachesize < (offset + sizeof (struct cache_file_new))
Packit Service 82fcde
	      || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
Packit Service 82fcde
			 sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
Packit Service 82fcde
	    cache_new = (void *) -1;
Packit Service 82fcde
	}
Packit Service 82fcde
      else if (file != MAP_FAILED && cachesize > sizeof *cache_new
Packit Service 82fcde
	       && memcmp (file, CACHEMAGIC_VERSION_NEW,
Packit Service 82fcde
			  sizeof CACHEMAGIC_VERSION_NEW - 1) == 0)
Packit Service 82fcde
	{
Packit Service 82fcde
	  cache_new = file;
Packit Service 82fcde
	  cache = file;
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (file != MAP_FAILED)
Packit Service 82fcde
	    __munmap (file, cachesize);
Packit Service 82fcde
	  cache = (void *) -1;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      assert (cache != NULL);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (cache == (void *) -1)
Packit Service 82fcde
    /* Previously looked for the cache file and didn't find it.  */
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  best = NULL;
Packit Service 82fcde
Packit Service 82fcde
  if (cache_new != (void *) -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      uint64_t platform;
Packit Service 82fcde
Packit Service 82fcde
      /* This is where the strings start.  */
Packit Service 82fcde
      cache_data = (const char *) cache_new;
Packit Service 82fcde
Packit Service 82fcde
      /* Now we can compute how large the string table is.  */
Packit Service 82fcde
      cache_data_size = (const char *) cache + cachesize - cache_data;
Packit Service 82fcde
Packit Service 82fcde
      platform = _dl_string_platform (GLRO(dl_platform));
Packit Service 82fcde
      if (platform != (uint64_t) -1)
Packit Service 82fcde
	platform = 1ULL << platform;
Packit Service 82fcde
Packit Service 82fcde
      uint64_t hwcap_mask = GET_HWCAP_MASK();
Packit Service 82fcde
Packit Service 82fcde
#define _DL_HWCAP_TLS_MASK (1LL << 63)
Packit Service 82fcde
      uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask)
Packit Service 82fcde
				 | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
Packit Service 82fcde
Packit Service 82fcde
      /* Only accept hwcap if it's for the right platform.  */
Packit Service 82fcde
#define HWCAP_CHECK \
Packit Service 82fcde
      if (lib->hwcap & hwcap_exclude)					      \
Packit Service 82fcde
	continue;							      \
Packit Service 82fcde
      if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion))	      \
Packit Service 82fcde
	continue;							      \
Packit Service 82fcde
      if (_DL_PLATFORMS_COUNT						      \
Packit Service 82fcde
	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0			      \
Packit Service 82fcde
	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform)		      \
Packit Service 82fcde
	continue
Packit Service 82fcde
      SEARCH_CACHE (cache_new);
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* This is where the strings start.  */
Packit Service 82fcde
      cache_data = (const char *) &cache->libs[cache->nlibs];
Packit Service 82fcde
Packit Service 82fcde
      /* Now we can compute how large the string table is.  */
Packit Service 82fcde
      cache_data_size = (const char *) cache + cachesize - cache_data;
Packit Service 82fcde
Packit Service 82fcde
#undef HWCAP_CHECK
Packit Service 82fcde
#define HWCAP_CHECK do {} while (0)
Packit Service 82fcde
      SEARCH_CACHE (cache);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Print our result if wanted.  */
Packit Service 82fcde
  if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)
Packit Service 82fcde
      && best != NULL)
Packit Service 82fcde
    _dl_debug_printf ("  trying file=%s\n", best);
Packit Service 82fcde
Packit Service 82fcde
  if (best == NULL)
Packit Service 82fcde
    return NULL;
Packit Service 82fcde
Packit Service 82fcde
  /* The double copy is *required* since malloc may be interposed
Packit Service 82fcde
     and call dlopen itself whose completion would unmap the data
Packit Service 82fcde
     we are accessing. Therefore we must make the copy of the
Packit Service 82fcde
     mapping data without using malloc.  */
Packit Service 82fcde
  char *temp;
Packit Service 82fcde
  temp = alloca (strlen (best) + 1);
Packit Service 82fcde
  strcpy (temp, best);
Packit Service 82fcde
  return __strdup (temp);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
#ifndef MAP_COPY
Packit Service 82fcde
/* If the system does not support MAP_COPY we cannot leave the file open
Packit Service 82fcde
   all the time since this would create problems when the file is replaced.
Packit Service 82fcde
   Therefore we provide this function to close the file and open it again
Packit Service 82fcde
   once needed.  */
Packit Service 82fcde
void
Packit Service 82fcde
_dl_unload_cache (void)
Packit Service 82fcde
{
Packit Service 82fcde
  if (cache != NULL && cache != (struct cache_file *) -1)
Packit Service 82fcde
    {
Packit Service 82fcde
      __munmap (cache, cachesize);
Packit Service 82fcde
      cache = NULL;
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
#endif