Blame elf/dl-cache.c

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