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