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 Service c82647
/* 1 if cache_data + PTR points into the cache.  */
Packit Service c82647
#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
Packit Service c82647
Packit Service c82647
#define SEARCH_CACHE(cache) \
Packit Service c82647
/* We use binary search since the table is sorted in the cache file.	      \
Packit Service c82647
   The first matching entry in the table is returned.			      \
Packit Service c82647
   It is important to use the same algorithm as used while generating	      \
Packit Service c82647
   the cache file.  */							      \
Packit Service c82647
do									      \
Packit Service c82647
  {									      \
Packit Service c82647
    left = 0;								      \
Packit Service c82647
    right = cache->nlibs - 1;						      \
Packit Service c82647
									      \
Packit Service c82647
    while (left <= right)						      \
Packit Service c82647
      {									      \
Packit Service c82647
	__typeof__ (cache->libs[0].key) key;				      \
Packit Service c82647
									      \
Packit Service c82647
	middle = (left + right) / 2;					      \
Packit Service c82647
									      \
Packit Service c82647
	key = cache->libs[middle].key;					      \
Packit Service c82647
									      \
Packit Service c82647
	/* Make sure string table indices are not bogus before using	      \
Packit Service c82647
	   them.  */							      \
Packit Service c82647
	if (! _dl_cache_verify_ptr (key))				      \
Packit Service c82647
	  {								      \
Packit Service c82647
	    cmpres = 1;							      \
Packit Service c82647
	    break;							      \
Packit Service c82647
	  }								      \
Packit Service c82647
									      \
Packit Service c82647
	/* Actually compare the entry with the key.  */			      \
Packit Service c82647
	cmpres = _dl_cache_libcmp (name, cache_data + key);		      \
Packit Service c82647
	if (__glibc_unlikely (cmpres == 0))				      \
Packit Service c82647
	  {								      \
Packit Service c82647
	    /* Found it.  LEFT now marks the last entry for which we	      \
Packit Service c82647
	       know the name is correct.  */				      \
Packit Service c82647
	    left = middle;						      \
Packit Service c82647
									      \
Packit Service c82647
	    /* There might be entries with this name before the one we	      \
Packit Service c82647
	       found.  So we have to find the beginning.  */		      \
Packit Service c82647
	    while (middle > 0)						      \
Packit Service c82647
	      {								      \
Packit Service c82647
		__typeof__ (cache->libs[0].key) key;			      \
Packit Service c82647
									      \
Packit Service c82647
		key = cache->libs[middle - 1].key;			      \
Packit Service c82647
		/* Make sure string table indices are not bogus before	      \
Packit Service c82647
		   using them.  */					      \
Packit Service c82647
		if (! _dl_cache_verify_ptr (key)			      \
Packit Service c82647
		    /* Actually compare the entry.  */			      \
Packit Service c82647
		    || _dl_cache_libcmp (name, cache_data + key) != 0)	      \
Packit Service c82647
		  break;						      \
Packit Service c82647
		--middle;						      \
Packit Service c82647
	      }								      \
Packit Service c82647
									      \
Packit Service c82647
	    do								      \
Packit Service c82647
	      {								      \
Packit Service c82647
		int flags;						      \
Packit Service c82647
		__typeof__ (cache->libs[0]) *lib = &cache->libs[middle];      \
Packit Service c82647
									      \
Packit Service c82647
		/* Only perform the name test if necessary.  */		      \
Packit Service c82647
		if (middle > left					      \
Packit Service c82647
		    /* We haven't seen this string so far.  Test whether the  \
Packit Service c82647
		       index is ok and whether the name matches.  Otherwise   \
Packit Service c82647
		       we are done.  */					      \
Packit Service c82647
		    && (! _dl_cache_verify_ptr (lib->key)		      \
Packit Service c82647
			|| (_dl_cache_libcmp (name, cache_data + lib->key)    \
Packit Service c82647
			    != 0)))					      \
Packit Service c82647
		  break;						      \
Packit Service c82647
									      \
Packit Service c82647
		flags = lib->flags;					      \
Packit Service c82647
		if (_dl_cache_check_flags (flags)			      \
Packit Service c82647
		    && _dl_cache_verify_ptr (lib->value))		      \
Packit Service c82647
		  {							      \
Packit Service c82647
		    if (best == NULL || flags == GLRO(dl_correct_cache_id))   \
Packit Service c82647
		      {							      \
Packit Service c82647
			HWCAP_CHECK;					      \
Packit Service c82647
			best = cache_data + lib->value;			      \
Packit Service c82647
									      \
Packit Service c82647
			if (flags == GLRO(dl_correct_cache_id))		      \
Packit Service c82647
			  /* We've found an exact match for the shared	      \
Packit Service c82647
			     object and no general `ELF' release.  Stop	      \
Packit Service c82647
			     searching.  */				      \
Packit Service c82647
			  break;					      \
Packit Service c82647
		      }							      \
Packit Service c82647
		  }							      \
Packit Service c82647
	      }								      \
Packit Service c82647
	    while (++middle <= right);					      \
Packit Service c82647
	    break;							      \
Packit Service c82647
	}								      \
Packit Service c82647
									      \
Packit Service c82647
	if (cmpres < 0)							      \
Packit Service c82647
	  left = middle + 1;						      \
Packit Service c82647
	else								      \
Packit Service c82647
	  right = middle - 1;						      \
Packit Service c82647
      }									      \
Packit Service c82647
  }									      \
Packit Service c82647
while (0)
Packit Service 573cf7
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 Service c82647
  int left, right, middle;
Packit Service c82647
  int cmpres;
Packit Service c82647
  const char *cache_data;
Packit Service c82647
  uint32_t cache_data_size;
Packit Service c82647
  const char *best;
Packit Service c82647
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 Service 69769d
	 - only the new format
Packit 6c4009
	 - the old libc5/glibc2.0/2.1 format
Packit 6c4009
	 - the old format with the new format in it
Packit 6c4009
	 The following checks if the cache contains any of these formats.  */
Packit Service 69769d
      if (file != MAP_FAILED && cachesize > sizeof *cache_new
Packit Service 69769d
	  && memcmp (file, CACHEMAGIC_VERSION_NEW,
Packit Service 69769d
		     sizeof CACHEMAGIC_VERSION_NEW - 1) == 0
Packit Service 4fa882
	  /* Check for corruption, avoiding overflow.  */
Packit Service 69769d
	  && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)
Packit Service 69769d
	      >= ((struct cache_file_new *) file)->nlibs))
Packit Service 69769d
	{
Packit Service 69769d
	  cache_new = file;
Packit Service 69769d
	  cache = file;
Packit Service 69769d
	}
Packit Service 69769d
      else if (file != MAP_FAILED && cachesize > sizeof *cache
Packit Service 69769d
	       && memcmp (file, CACHEMAGIC, sizeof CACHEMAGIC - 1) == 0
Packit Service 69769d
	       /* Check for corruption, avoiding overflow.  */
Packit Service 69769d
	       && ((cachesize - sizeof *cache) / sizeof (struct file_entry)
Packit Service 69769d
		   >= ((struct cache_file *) file)->nlibs))
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 Service c82647
	    cache_new = (void *) -1;
Packit Service c82647
	}
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 Service c82647
  best = NULL;
Packit Service c82647
Packit 6c4009
  if (cache_new != (void *) -1)
Packit 6c4009
    {
Packit Service c82647
      uint64_t platform;
Packit Service c82647
Packit Service c82647
      /* This is where the strings start.  */
Packit Service c82647
      cache_data = (const char *) cache_new;
Packit Service c82647
Packit Service c82647
      /* Now we can compute how large the string table is.  */
Packit Service c82647
      cache_data_size = (const char *) cache + cachesize - cache_data;
Packit Service c82647
Packit Service c82647
      platform = _dl_string_platform (GLRO(dl_platform));
Packit Service c82647
      if (platform != (uint64_t) -1)
Packit Service c82647
	platform = 1ULL << platform;
Packit Service c82647
Packit Service c82647
      uint64_t hwcap_mask = GET_HWCAP_MASK();
Packit Service c82647
Packit Service c82647
#define _DL_HWCAP_TLS_MASK (1LL << 63)
Packit Service c82647
      uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask)
Packit Service c82647
				 | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
Packit Service c82647
Packit Service c82647
      /* Only accept hwcap if it's for the right platform.  */
Packit Service c82647
#define HWCAP_CHECK \
Packit Service c82647
      if (lib->hwcap & hwcap_exclude)					      \
Packit Service c82647
	continue;							      \
Packit Service c82647
      if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion))	      \
Packit Service c82647
	continue;							      \
Packit Service c82647
      if (_DL_PLATFORMS_COUNT						      \
Packit Service c82647
	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0			      \
Packit Service c82647
	  && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform)		      \
Packit Service c82647
	continue
Packit Service c82647
      SEARCH_CACHE (cache_new);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit Service c82647
      /* This is where the strings start.  */
Packit Service c82647
      cache_data = (const char *) &cache->libs[cache->nlibs];
Packit Service c82647
Packit Service c82647
      /* Now we can compute how large the string table is.  */
Packit Service c82647
      cache_data_size = (const char *) cache + cachesize - cache_data;
Packit Service c82647
Packit Service c82647
#undef HWCAP_CHECK
Packit Service c82647
#define HWCAP_CHECK do {} while (0)
Packit Service c82647
      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