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