Blame elf/cache.c

Packit 6c4009
/* Copyright (C) 1999-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Andreas Jaeger <aj@suse.de>, 1999.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program 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
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <dirent.h>
Packit 6c4009
#include <inttypes.h>
Packit 6c4009
#include <libgen.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
#include <sys/fcntl.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
Packit 6c4009
#include <ldconfig.h>
Packit 6c4009
#include <dl-cache.h>
Packit 6c4009
Packit 6c4009
struct cache_entry
Packit 6c4009
{
Packit 6c4009
  char *lib;			/* Library name.  */
Packit 6c4009
  char *path;			/* Path to find library.  */
Packit 6c4009
  int flags;			/* Flags to indicate kind of library.  */
Packit 6c4009
  unsigned int osversion;	/* Required OS version.  */
Packit 6c4009
  uint64_t hwcap;		/* Important hardware capabilities.  */
Packit 6c4009
  int bits_hwcap;		/* Number of bits set in hwcap.  */
Packit 6c4009
  struct cache_entry *next;	/* Next entry in list.  */
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* List of all cache entries.  */
Packit 6c4009
static struct cache_entry *entries;
Packit 6c4009
Packit 6c4009
static const char *flag_descr[] =
Packit 6c4009
{ "libc4", "ELF", "libc5", "libc6"};
Packit 6c4009
Packit 6c4009
/* Print a single entry.  */
Packit 6c4009
static void
Packit 6c4009
print_entry (const char *lib, int flag, unsigned int osversion,
Packit 6c4009
	     uint64_t hwcap, const char *key)
Packit 6c4009
{
Packit 6c4009
  printf ("\t%s (", lib);
Packit 6c4009
  switch (flag & FLAG_TYPE_MASK)
Packit 6c4009
    {
Packit 6c4009
    case FLAG_LIBC4:
Packit 6c4009
    case FLAG_ELF:
Packit 6c4009
    case FLAG_ELF_LIBC5:
Packit 6c4009
    case FLAG_ELF_LIBC6:
Packit 6c4009
      fputs (flag_descr[flag & FLAG_TYPE_MASK], stdout);
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      fputs (_("unknown"), stdout);
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  switch (flag & FLAG_REQUIRED_MASK)
Packit 6c4009
    {
Packit 6c4009
    case FLAG_SPARC_LIB64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_IA64_LIB64:
Packit 6c4009
      fputs (",IA-64", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_X8664_LIB64:
Packit 6c4009
      fputs (",x86-64", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_S390_LIB64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_POWERPC_LIB64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN32:
Packit 6c4009
      fputs (",N32", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN64:
Packit 6c4009
      fputs (",64bit", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_X8664_LIBX32:
Packit 6c4009
      fputs (",x32", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_ARM_LIBHF:
Packit 6c4009
      fputs (",hard-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_AARCH64_LIB64:
Packit 6c4009
      fputs (",AArch64", stdout);
Packit 6c4009
      break;
Packit 6c4009
    /* Uses the ARM soft-float ABI.  */
Packit 6c4009
    case FLAG_ARM_LIBSF:
Packit 6c4009
      fputs (",soft-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS_LIB32_NAN2008:
Packit 6c4009
      fputs (",nan2008", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN32_NAN2008:
Packit 6c4009
      fputs (",N32,nan2008", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_MIPS64_LIBN64_NAN2008:
Packit 6c4009
      fputs (",64bit,nan2008", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_RISCV_FLOAT_ABI_SOFT:
Packit 6c4009
      fputs (",soft-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case FLAG_RISCV_FLOAT_ABI_DOUBLE:
Packit 6c4009
      fputs (",double-float", stdout);
Packit 6c4009
      break;
Packit 6c4009
    case 0:
Packit 6c4009
      break;
Packit 6c4009
    default:
Packit 6c4009
      printf (",%d", flag & FLAG_REQUIRED_MASK);
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
  if (hwcap != 0)
Packit 6c4009
    printf (", hwcap: %#.16" PRIx64, hwcap);
Packit 6c4009
  if (osversion != 0)
Packit 6c4009
    {
Packit 6c4009
      static const char *const abi_tag_os[] =
Packit 6c4009
      {
Packit 6c4009
	[0] = "Linux",
Packit 6c4009
	[1] = "Hurd",
Packit 6c4009
	[2] = "Solaris",
Packit 6c4009
	[3] = "FreeBSD",
Packit 6c4009
	[4] = "kNetBSD",
Packit 6c4009
	[5] = "Syllable",
Packit 6c4009
	[6] = N_("Unknown OS")
Packit 6c4009
      };
Packit 6c4009
#define MAXTAG (sizeof abi_tag_os / sizeof abi_tag_os[0] - 1)
Packit 6c4009
      unsigned int os = osversion >> 24;
Packit 6c4009
Packit 6c4009
      printf (_(", OS ABI: %s %d.%d.%d"),
Packit 6c4009
	      _(abi_tag_os[os > MAXTAG ? MAXTAG : os]),
Packit 6c4009
	      (osversion >> 16) & 0xff,
Packit 6c4009
	      (osversion >> 8) & 0xff,
Packit 6c4009
	      osversion & 0xff);
Packit 6c4009
    }
Packit 6c4009
  printf (") => %s\n", key);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Print the whole cache file, if a file contains the new cache format
Packit 6c4009
   hidden in the old one, print the contents of the new format.  */
Packit 6c4009
void
Packit 6c4009
print_cache (const char *cache_name)
Packit 6c4009
{
Packit 6c4009
  int fd = open (cache_name, O_RDONLY);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Can't open cache file %s\n"), cache_name);
Packit 6c4009
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (fstat64 (fd, &st) < 0
Packit 6c4009
      /* No need to map the file if it is empty.  */
Packit 6c4009
      || st.st_size == 0)
Packit 6c4009
    {
Packit 6c4009
      close (fd);
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct cache_file *cache
Packit 6c4009
    = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Packit 6c4009
  if (cache == MAP_FAILED)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("mmap of cache file failed.\n"));
Packit 6c4009
Packit 6c4009
  size_t cache_size = st.st_size;
Packit 6c4009
  if (cache_size < sizeof (struct cache_file))
Packit 6c4009
    error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
Packit 6c4009
Packit 6c4009
  struct cache_file_new *cache_new = NULL;
Packit 6c4009
  const char *cache_data;
Packit 6c4009
  int format = 0;
Packit 6c4009
Packit 6c4009
  if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
Packit 6c4009
    {
Packit 6c4009
      /* This can only be the new format without the old one.  */
Packit 6c4009
      cache_new = (struct cache_file_new *) cache;
Packit 6c4009
Packit 6c4009
      if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
Packit 6c4009
	  || memcmp (cache_new->version, CACHE_VERSION,
Packit 6c4009
		      sizeof CACHE_VERSION - 1))
Packit 6c4009
	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
Packit 6c4009
      format = 1;
Packit 6c4009
      /* This is where the strings start.  */
Packit 6c4009
      cache_data = (const char *) cache_new;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      size_t offset = ALIGN_CACHE (sizeof (struct cache_file)
Packit 6c4009
				   + (cache->nlibs
Packit 6c4009
				      * sizeof (struct file_entry)));
Packit 6c4009
      /* This is where the strings start.  */
Packit 6c4009
      cache_data = (const char *) &cache->libs[cache->nlibs];
Packit 6c4009
Packit 6c4009
      /* Check for a new cache embedded in the old format.  */
Packit 6c4009
      if (cache_size >
Packit 6c4009
	  (offset + sizeof (struct cache_file_new)))
Packit 6c4009
	{
Packit 6c4009
Packit 6c4009
	  cache_new = (struct cache_file_new *) ((void *)cache + offset);
Packit 6c4009
Packit 6c4009
	  if (memcmp (cache_new->magic, CACHEMAGIC_NEW,
Packit 6c4009
		      sizeof CACHEMAGIC_NEW - 1) == 0
Packit 6c4009
	      && memcmp (cache_new->version, CACHE_VERSION,
Packit 6c4009
			 sizeof CACHE_VERSION - 1) == 0)
Packit 6c4009
	    {
Packit 6c4009
	      cache_data = (const char *) cache_new;
Packit 6c4009
	      format = 1;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (format == 0)
Packit 6c4009
    {
Packit 6c4009
      printf (_("%d libs found in cache `%s'\n"), cache->nlibs, cache_name);
Packit 6c4009
Packit 6c4009
      /* Print everything.  */
Packit 6c4009
      for (unsigned int i = 0; i < cache->nlibs; i++)
Packit 6c4009
	print_entry (cache_data + cache->libs[i].key,
Packit 6c4009
		     cache->libs[i].flags, 0, 0,
Packit 6c4009
		     cache_data + cache->libs[i].value);
Packit 6c4009
    }
Packit 6c4009
  else if (format == 1)
Packit 6c4009
    {
Packit 6c4009
      printf (_("%d libs found in cache `%s'\n"),
Packit 6c4009
	      cache_new->nlibs, cache_name);
Packit 6c4009
Packit 6c4009
      /* Print everything.  */
Packit 6c4009
      for (unsigned int i = 0; i < cache_new->nlibs; i++)
Packit 6c4009
	print_entry (cache_data + cache_new->libs[i].key,
Packit 6c4009
		     cache_new->libs[i].flags,
Packit 6c4009
		     cache_new->libs[i].osversion,
Packit 6c4009
		     cache_new->libs[i].hwcap,
Packit 6c4009
		     cache_data + cache_new->libs[i].value);
Packit 6c4009
    }
Packit 6c4009
  /* Cleanup.  */
Packit 6c4009
  munmap (cache, cache_size);
Packit 6c4009
  close (fd);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Initialize cache data structures.  */
Packit 6c4009
void
Packit 6c4009
init_cache (void)
Packit 6c4009
{
Packit 6c4009
  entries = NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
compare (const struct cache_entry *e1, const struct cache_entry *e2)
Packit 6c4009
{
Packit 6c4009
  /* We need to swap entries here to get the correct sort order.  */
Packit 6c4009
  int res = _dl_cache_libcmp (e2->lib, e1->lib);
Packit 6c4009
  if (res == 0)
Packit 6c4009
    {
Packit 6c4009
      if (e1->flags < e2->flags)
Packit 6c4009
	return 1;
Packit 6c4009
      else if (e1->flags > e2->flags)
Packit 6c4009
	return -1;
Packit 6c4009
      /* Sort by most specific hwcap.  */
Packit 6c4009
      else if (e2->bits_hwcap > e1->bits_hwcap)
Packit 6c4009
	return 1;
Packit 6c4009
      else if (e2->bits_hwcap < e1->bits_hwcap)
Packit 6c4009
	return -1;
Packit 6c4009
      else if (e2->hwcap > e1->hwcap)
Packit 6c4009
	return 1;
Packit 6c4009
      else if (e2->hwcap < e1->hwcap)
Packit 6c4009
	return -1;
Packit 6c4009
      if (e2->osversion > e1->osversion)
Packit 6c4009
	return 1;
Packit 6c4009
      if (e2->osversion < e1->osversion)
Packit 6c4009
	return -1;
Packit 6c4009
    }
Packit 6c4009
  return res;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Save the contents of the cache.  */
Packit 6c4009
void
Packit 6c4009
save_cache (const char *cache_name)
Packit 6c4009
{
Packit 6c4009
  /* The cache entries are sorted already, save them in this order. */
Packit 6c4009
Packit 6c4009
  /* Count the length of all strings.  */
Packit 6c4009
  /* The old format doesn't contain hwcap entries and doesn't contain
Packit 6c4009
     libraries in subdirectories with hwcaps entries.  Count therefore
Packit 6c4009
     also all entries with hwcap == 0.  */
Packit 6c4009
  size_t total_strlen = 0;
Packit 6c4009
  struct cache_entry *entry;
Packit 6c4009
  /* Number of cache entries.  */
Packit 6c4009
  int cache_entry_count = 0;
Packit 6c4009
  /* Number of normal cache entries.  */
Packit 6c4009
  int cache_entry_old_count = 0;
Packit 6c4009
Packit 6c4009
  for (entry = entries; entry != NULL; entry = entry->next)
Packit 6c4009
    {
Packit 6c4009
      /* Account the final NULs.  */
Packit 6c4009
      total_strlen += strlen (entry->lib) + strlen (entry->path) + 2;
Packit 6c4009
      ++cache_entry_count;
Packit 6c4009
      if (entry->hwcap == 0)
Packit 6c4009
	++cache_entry_old_count;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Create the on disk cache structure.  */
Packit 6c4009
  struct cache_file *file_entries = NULL;
Packit 6c4009
  size_t file_entries_size = 0;
Packit 6c4009
Packit 6c4009
  if (opt_format != 2)
Packit 6c4009
    {
Packit 6c4009
      /* struct cache_file_new is 64-bit aligned on some arches while
Packit 6c4009
	 only 32-bit aligned on other arches.  Duplicate last old
Packit 6c4009
	 cache entry so that new cache in ld.so.cache can be used by
Packit 6c4009
	 both.  */
Packit 6c4009
      if (opt_format != 0)
Packit 6c4009
	cache_entry_old_count = (cache_entry_old_count + 1) & ~1;
Packit 6c4009
Packit 6c4009
      /* And the list of all entries in the old format.  */
Packit 6c4009
      file_entries_size = sizeof (struct cache_file)
Packit 6c4009
	+ cache_entry_old_count * sizeof (struct file_entry);
Packit 6c4009
      file_entries = xmalloc (file_entries_size);
Packit 6c4009
Packit 6c4009
      /* Fill in the header.  */
Packit 6c4009
      memset (file_entries, '\0', sizeof (struct cache_file));
Packit 6c4009
      memcpy (file_entries->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1);
Packit 6c4009
Packit 6c4009
      file_entries->nlibs = cache_entry_old_count;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct cache_file_new *file_entries_new = NULL;
Packit 6c4009
  size_t file_entries_new_size = 0;
Packit 6c4009
Packit 6c4009
  if (opt_format != 0)
Packit 6c4009
    {
Packit 6c4009
      /* And the list of all entries in the new format.  */
Packit 6c4009
      file_entries_new_size = sizeof (struct cache_file_new)
Packit 6c4009
	+ cache_entry_count * sizeof (struct file_entry_new);
Packit 6c4009
      file_entries_new = xmalloc (file_entries_new_size);
Packit 6c4009
Packit 6c4009
      /* Fill in the header.  */
Packit 6c4009
      memset (file_entries_new, '\0', sizeof (struct cache_file_new));
Packit 6c4009
      memcpy (file_entries_new->magic, CACHEMAGIC_NEW,
Packit 6c4009
	      sizeof CACHEMAGIC_NEW - 1);
Packit 6c4009
      memcpy (file_entries_new->version, CACHE_VERSION,
Packit 6c4009
	      sizeof CACHE_VERSION - 1);
Packit 6c4009
Packit 6c4009
      file_entries_new->nlibs = cache_entry_count;
Packit 6c4009
      file_entries_new->len_strings = total_strlen;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Pad for alignment of cache_file_new.  */
Packit 6c4009
  size_t pad = ALIGN_CACHE (file_entries_size) - file_entries_size;
Packit 6c4009
Packit 6c4009
  /* If we have both formats, we hide the new format in the strings
Packit 6c4009
     table, we have to adjust all string indices for this so that
Packit 6c4009
     old libc5/glibc 2 dynamic linkers just ignore them.  */
Packit 6c4009
  unsigned int str_offset;
Packit 6c4009
  if (opt_format != 0)
Packit 6c4009
    str_offset = file_entries_new_size;
Packit 6c4009
  else
Packit 6c4009
    str_offset = 0;
Packit 6c4009
Packit 6c4009
  /* An array for all strings.  */
Packit 6c4009
  char *strings = xmalloc (total_strlen);
Packit 6c4009
  char *str = strings;
Packit 6c4009
  int idx_old;
Packit 6c4009
  int idx_new;
Packit 6c4009
Packit 6c4009
  for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL;
Packit 6c4009
       entry = entry->next, ++idx_new)
Packit 6c4009
    {
Packit 6c4009
      /* First the library.  */
Packit 6c4009
      if (opt_format != 2 && entry->hwcap == 0)
Packit 6c4009
	{
Packit 6c4009
	  file_entries->libs[idx_old].flags = entry->flags;
Packit 6c4009
	  /* XXX: Actually we can optimize here and remove duplicates.  */
Packit 6c4009
	  file_entries->libs[idx_old].key = str_offset + pad;
Packit 6c4009
	}
Packit 6c4009
      if (opt_format != 0)
Packit 6c4009
	{
Packit 6c4009
	  /* We could subtract file_entries_new_size from str_offset -
Packit 6c4009
	     not doing so makes the code easier, the string table
Packit 6c4009
	     always begins at the beginning of the new cache
Packit 6c4009
	     struct.  */
Packit 6c4009
	  file_entries_new->libs[idx_new].flags = entry->flags;
Packit 6c4009
	  file_entries_new->libs[idx_new].osversion = entry->osversion;
Packit 6c4009
	  file_entries_new->libs[idx_new].hwcap = entry->hwcap;
Packit 6c4009
	  file_entries_new->libs[idx_new].key = str_offset;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      size_t len = strlen (entry->lib) + 1;
Packit 6c4009
      str = mempcpy (str, entry->lib, len);
Packit 6c4009
      str_offset += len;
Packit 6c4009
      /* Then the path.  */
Packit 6c4009
      if (opt_format != 2 && entry->hwcap == 0)
Packit 6c4009
	file_entries->libs[idx_old].value = str_offset + pad;
Packit 6c4009
      if (opt_format != 0)
Packit 6c4009
	file_entries_new->libs[idx_new].value = str_offset;
Packit 6c4009
      len = strlen (entry->path) + 1;
Packit 6c4009
      str = mempcpy (str, entry->path, len);
Packit 6c4009
      str_offset += len;
Packit 6c4009
      /* Ignore entries with hwcap for old format.  */
Packit 6c4009
      if (entry->hwcap == 0)
Packit 6c4009
	++idx_old;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Duplicate last old cache entry if needed.  */
Packit 6c4009
  if (opt_format != 2
Packit 6c4009
      && idx_old < cache_entry_old_count)
Packit 6c4009
    file_entries->libs[idx_old] = file_entries->libs[idx_old - 1];
Packit 6c4009
Packit 6c4009
  /* Write out the cache.  */
Packit 6c4009
Packit 6c4009
  /* Write cache first to a temporary file and rename it later.  */
Packit 6c4009
  char *temp_name = xmalloc (strlen (cache_name) + 2);
Packit 6c4009
  sprintf (temp_name, "%s~", cache_name);
Packit 6c4009
Packit 6c4009
  /* Create file.  */
Packit 6c4009
  int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
Packit 6c4009
		 S_IRUSR|S_IWUSR);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Can't create temporary cache file %s"),
Packit 6c4009
	   temp_name);
Packit 6c4009
Packit 6c4009
  /* Write contents.  */
Packit 6c4009
  if (opt_format != 2)
Packit 6c4009
    {
Packit 6c4009
      if (write (fd, file_entries, file_entries_size)
Packit 6c4009
	  != (ssize_t) file_entries_size)
Packit 6c4009
	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
    }
Packit 6c4009
  if (opt_format != 0)
Packit 6c4009
    {
Packit 6c4009
      /* Align cache.  */
Packit 6c4009
      if (opt_format != 2)
Packit 6c4009
	{
Packit 6c4009
	  char zero[pad];
Packit 6c4009
	  memset (zero, '\0', pad);
Packit 6c4009
	  if (write (fd, zero, pad) != (ssize_t) pad)
Packit 6c4009
	    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
	}
Packit 6c4009
      if (write (fd, file_entries_new, file_entries_new_size)
Packit 6c4009
	  != (ssize_t) file_entries_new_size)
Packit 6c4009
	error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (write (fd, strings, total_strlen) != (ssize_t) total_strlen)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
Packit 6c4009
  /* Make sure user can always read cache file */
Packit 6c4009
  if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR))
Packit 6c4009
    error (EXIT_FAILURE, errno,
Packit 6c4009
	   _("Changing access rights of %s to %#o failed"), temp_name,
Packit 6c4009
	   S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR);
Packit 6c4009
Packit 6c4009
  /* Make sure that data is written to disk.  */
Packit 6c4009
  if (fsync (fd) != 0 || close (fd) != 0)
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Writing of cache data failed"));
Packit 6c4009
Packit 6c4009
  /* Move temporary to its final location.  */
Packit 6c4009
  if (rename (temp_name, cache_name))
Packit 6c4009
    error (EXIT_FAILURE, errno, _("Renaming of %s to %s failed"), temp_name,
Packit 6c4009
	   cache_name);
Packit 6c4009
Packit 6c4009
  /* Free all allocated memory.  */
Packit 6c4009
  free (file_entries_new);
Packit 6c4009
  free (file_entries);
Packit 6c4009
  free (strings);
Packit 6c4009
Packit 6c4009
  while (entries)
Packit 6c4009
    {
Packit 6c4009
      entry = entries;
Packit 6c4009
      entries = entries->next;
Packit 6c4009
      free (entry);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Add one library to the cache.  */
Packit 6c4009
void
Packit 6c4009
add_to_cache (const char *path, const char *lib, int flags,
Packit 6c4009
	      unsigned int osversion, uint64_t hwcap)
Packit 6c4009
{
Packit 6c4009
  size_t liblen = strlen (lib) + 1;
Packit 6c4009
  size_t len = liblen + strlen (path) + 1;
Packit 6c4009
  struct cache_entry *new_entry
Packit 6c4009
    = xmalloc (sizeof (struct cache_entry) + liblen + len);
Packit 6c4009
Packit 6c4009
  new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen);
Packit 6c4009
  new_entry->path = new_entry->lib + liblen;
Packit 6c4009
  snprintf (new_entry->path, len, "%s/%s", path, lib);
Packit 6c4009
  new_entry->flags = flags;
Packit 6c4009
  new_entry->osversion = osversion;
Packit 6c4009
  new_entry->hwcap = hwcap;
Packit 6c4009
  new_entry->bits_hwcap = 0;
Packit 6c4009
Packit 6c4009
  /* Count the number of bits set in the masked value.  */
Packit 6c4009
  for (size_t i = 0;
Packit 6c4009
       (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i)
Packit 6c4009
    if ((hwcap & (1ULL << i)) != 0)
Packit 6c4009
      ++new_entry->bits_hwcap;
Packit 6c4009
Packit 6c4009
Packit 6c4009
  /* Keep the list sorted - search for right place to insert.  */
Packit 6c4009
  struct cache_entry *ptr = entries;
Packit 6c4009
  struct cache_entry *prev = entries;
Packit 6c4009
  while (ptr != NULL)
Packit 6c4009
    {
Packit 6c4009
      if (compare (ptr, new_entry) > 0)
Packit 6c4009
	break;
Packit 6c4009
      prev = ptr;
Packit 6c4009
      ptr = ptr->next;
Packit 6c4009
    }
Packit 6c4009
  /* Is this the first entry?  */
Packit 6c4009
  if (ptr == entries)
Packit 6c4009
    {
Packit 6c4009
      new_entry->next = entries;
Packit 6c4009
      entries = new_entry;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      new_entry->next = prev->next;
Packit 6c4009
      prev->next = new_entry;
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Auxiliary cache.  */
Packit 6c4009
Packit 6c4009
struct aux_cache_entry_id
Packit 6c4009
{
Packit 6c4009
  uint64_t ino;
Packit 6c4009
  uint64_t ctime;
Packit 6c4009
  uint64_t size;
Packit 6c4009
  uint64_t dev;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct aux_cache_entry
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;
Packit 6c4009
  int flags;
Packit 6c4009
  unsigned int osversion;
Packit 6c4009
  int used;
Packit 6c4009
  char *soname;
Packit 6c4009
  struct aux_cache_entry *next;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
#define AUX_CACHEMAGIC		"glibc-ld.so.auxcache-1.0"
Packit 6c4009
Packit 6c4009
struct aux_cache_file_entry
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;	/* Unique id of entry.  */
Packit 6c4009
  int32_t flags;		/* This is 1 for an ELF library.  */
Packit 6c4009
  uint32_t soname;		/* String table indice.  */
Packit 6c4009
  uint32_t osversion;		/* Required OS version.	 */
Packit 6c4009
  int32_t pad;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* ldconfig maintains an auxiliary cache file that allows
Packit 6c4009
   only reading those libraries that have changed since the last iteration.
Packit 6c4009
   For this for each library some information is cached in the auxiliary
Packit 6c4009
   cache.  */
Packit 6c4009
struct aux_cache_file
Packit 6c4009
{
Packit 6c4009
  char magic[sizeof AUX_CACHEMAGIC - 1];
Packit 6c4009
  uint32_t nlibs;		/* Number of entries.  */
Packit 6c4009
  uint32_t len_strings;		/* Size of string table. */
Packit 6c4009
  struct aux_cache_file_entry libs[0]; /* Entries describing libraries.  */
Packit 6c4009
  /* After this the string table of size len_strings is found.	*/
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static const unsigned int primes[] =
Packit 6c4009
{
Packit 6c4009
  1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139,
Packit 6c4009
  524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393,
Packit 6c4009
  67108859, 134217689, 268435399, 536870909, 1073741789, 2147483647
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static size_t aux_hash_size;
Packit 6c4009
static struct aux_cache_entry **aux_hash;
Packit 6c4009
Packit 6c4009
/* Simplistic hash function for aux_cache_entry_id.  */
Packit 6c4009
static unsigned int
Packit 6c4009
aux_cache_entry_id_hash (struct aux_cache_entry_id *id)
Packit 6c4009
{
Packit 6c4009
  uint64_t ret = ((id->ino * 11 + id->ctime) * 11 + id->size) * 11 + id->dev;
Packit 6c4009
  return ret ^ (ret >> 32);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static size_t nextprime (size_t x)
Packit 6c4009
{
Packit 6c4009
  for (unsigned int i = 0; i < sizeof (primes) / sizeof (primes[0]); ++i)
Packit 6c4009
    if (primes[i] >= x)
Packit 6c4009
      return primes[i];
Packit 6c4009
  return x;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
init_aux_cache (void)
Packit 6c4009
{
Packit 6c4009
  aux_hash_size = primes[3];
Packit 6c4009
  aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
search_aux_cache (struct stat64 *stat_buf, int *flags,
Packit 6c4009
		  unsigned int *osversion, char **soname)
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;
Packit 6c4009
  id.ino = (uint64_t) stat_buf->st_ino;
Packit 6c4009
  id.ctime = (uint64_t) stat_buf->st_ctime;
Packit 6c4009
  id.size = (uint64_t) stat_buf->st_size;
Packit 6c4009
  id.dev = (uint64_t) stat_buf->st_dev;
Packit 6c4009
Packit 6c4009
  unsigned int hash = aux_cache_entry_id_hash (&id;;
Packit 6c4009
  struct aux_cache_entry *entry;
Packit 6c4009
  for (entry = aux_hash[hash % aux_hash_size]; entry; entry = entry->next)
Packit 6c4009
    if (id.ino == entry->id.ino
Packit 6c4009
	&& id.ctime == entry->id.ctime
Packit 6c4009
	&& id.size == entry->id.size
Packit 6c4009
	&& id.dev == entry->id.dev)
Packit 6c4009
      {
Packit 6c4009
	*flags = entry->flags;
Packit 6c4009
	*osversion = entry->osversion;
Packit 6c4009
	if (entry->soname != NULL)
Packit 6c4009
	  *soname = xstrdup (entry->soname);
Packit 6c4009
	else
Packit 6c4009
	  *soname = NULL;
Packit 6c4009
	entry->used = 1;
Packit 6c4009
	return 1;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
insert_to_aux_cache (struct aux_cache_entry_id *id, int flags,
Packit 6c4009
		     unsigned int osversion, const char *soname, int used)
Packit 6c4009
{
Packit 6c4009
  size_t hash = aux_cache_entry_id_hash (id) % aux_hash_size;
Packit 6c4009
  struct aux_cache_entry *entry;
Packit 6c4009
  for (entry = aux_hash[hash]; entry; entry = entry->next)
Packit 6c4009
    if (id->ino == entry->id.ino
Packit 6c4009
	&& id->ctime == entry->id.ctime
Packit 6c4009
	&& id->size == entry->id.size
Packit 6c4009
	&& id->dev == entry->id.dev)
Packit 6c4009
      abort ();
Packit 6c4009
Packit 6c4009
  size_t len = soname ? strlen (soname) + 1 : 0;
Packit 6c4009
  entry = xmalloc (sizeof (struct aux_cache_entry) + len);
Packit 6c4009
  entry->id = *id;
Packit 6c4009
  entry->flags = flags;
Packit 6c4009
  entry->osversion = osversion;
Packit 6c4009
  entry->used = used;
Packit 6c4009
  if (soname != NULL)
Packit 6c4009
    entry->soname = memcpy ((char *) (entry + 1), soname, len);
Packit 6c4009
  else
Packit 6c4009
    entry->soname = NULL;
Packit 6c4009
  entry->next = aux_hash[hash];
Packit 6c4009
  aux_hash[hash] = entry;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
add_to_aux_cache (struct stat64 *stat_buf, int flags,
Packit 6c4009
		  unsigned int osversion, const char *soname)
Packit 6c4009
{
Packit 6c4009
  struct aux_cache_entry_id id;
Packit 6c4009
  id.ino = (uint64_t) stat_buf->st_ino;
Packit 6c4009
  id.ctime = (uint64_t) stat_buf->st_ctime;
Packit 6c4009
  id.size = (uint64_t) stat_buf->st_size;
Packit 6c4009
  id.dev = (uint64_t) stat_buf->st_dev;
Packit 6c4009
  insert_to_aux_cache (&id, flags, osversion, soname, 1);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Load auxiliary cache to search for unchanged entries.   */
Packit 6c4009
void
Packit 6c4009
load_aux_cache (const char *aux_cache_name)
Packit 6c4009
{
Packit 6c4009
  int fd = open (aux_cache_name, O_RDONLY);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    {
Packit 6c4009
      init_aux_cache ();
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (fstat64 (fd, &st) < 0 || st.st_size < sizeof (struct aux_cache_file))
Packit 6c4009
    {
Packit 6c4009
      close (fd);
Packit 6c4009
      init_aux_cache ();
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  size_t aux_cache_size = st.st_size;
Packit 6c4009
  struct aux_cache_file *aux_cache
Packit 6c4009
    = mmap (NULL, aux_cache_size, PROT_READ, MAP_PRIVATE, fd, 0);
Packit 6c4009
  if (aux_cache == MAP_FAILED
Packit 6c4009
      || aux_cache_size < sizeof (struct aux_cache_file)
Packit 6c4009
      || memcmp (aux_cache->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1)
Packit 6c4009
      || aux_cache_size != (sizeof(struct aux_cache_file) +
Packit 6c4009
			    aux_cache->nlibs * sizeof(struct aux_cache_file_entry) +
Packit 6c4009
			    aux_cache->len_strings))
Packit 6c4009
    {
Packit 6c4009
      close (fd);
Packit 6c4009
      init_aux_cache ();
Packit 6c4009
      return;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  aux_hash_size = nextprime (aux_cache->nlibs);
Packit 6c4009
  aux_hash = xcalloc (aux_hash_size, sizeof (struct aux_cache_entry *));
Packit 6c4009
Packit 6c4009
  const char *aux_cache_data
Packit 6c4009
    = (const char *) &aux_cache->libs[aux_cache->nlibs];
Packit 6c4009
  for (unsigned int i = 0; i < aux_cache->nlibs; ++i)
Packit 6c4009
    insert_to_aux_cache (&aux_cache->libs[i].id,
Packit 6c4009
			 aux_cache->libs[i].flags,
Packit 6c4009
			 aux_cache->libs[i].osversion,
Packit 6c4009
			 aux_cache->libs[i].soname == 0
Packit 6c4009
			 ? NULL : aux_cache_data + aux_cache->libs[i].soname,
Packit 6c4009
			 0);
Packit 6c4009
Packit 6c4009
  munmap (aux_cache, aux_cache_size);
Packit 6c4009
  close (fd);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Save the contents of the auxiliary cache.  */
Packit 6c4009
void
Packit 6c4009
save_aux_cache (const char *aux_cache_name)
Packit 6c4009
{
Packit 6c4009
  /* Count the length of all sonames.  We start with empty string.  */
Packit 6c4009
  size_t total_strlen = 1;
Packit 6c4009
  /* Number of cache entries.  */
Packit 6c4009
  int cache_entry_count = 0;
Packit 6c4009
Packit 6c4009
  for (size_t i = 0; i < aux_hash_size; ++i)
Packit 6c4009
    for (struct aux_cache_entry *entry = aux_hash[i];
Packit 6c4009
	 entry != NULL; entry = entry->next)
Packit 6c4009
      if (entry->used)
Packit 6c4009
	{
Packit 6c4009
	  ++cache_entry_count;
Packit 6c4009
	  if (entry->soname != NULL)
Packit 6c4009
	    total_strlen += strlen (entry->soname) + 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
  /* Auxiliary cache.  */
Packit 6c4009
  size_t file_entries_size
Packit 6c4009
    = sizeof (struct aux_cache_file)
Packit 6c4009
      + cache_entry_count * sizeof (struct aux_cache_file_entry);
Packit 6c4009
  struct aux_cache_file *file_entries
Packit 6c4009
    = xmalloc (file_entries_size + total_strlen);
Packit 6c4009
Packit 6c4009
  /* Fill in the header of the auxiliary cache.  */
Packit 6c4009
  memset (file_entries, '\0', sizeof (struct aux_cache_file));
Packit 6c4009
  memcpy (file_entries->magic, AUX_CACHEMAGIC, sizeof AUX_CACHEMAGIC - 1);
Packit 6c4009
Packit 6c4009
  file_entries->nlibs = cache_entry_count;
Packit 6c4009
  file_entries->len_strings = total_strlen;
Packit 6c4009
Packit 6c4009
  /* Initial String offset for auxiliary cache is always after the
Packit 6c4009
     special empty string.  */
Packit 6c4009
  unsigned int str_offset = 1;
Packit 6c4009
Packit 6c4009
  /* An array for all strings.  */
Packit 6c4009
  char *str = (char *) file_entries + file_entries_size;
Packit 6c4009
  *str++ = '\0';
Packit 6c4009
Packit 6c4009
  size_t idx = 0;
Packit 6c4009
  for (size_t i = 0; i < aux_hash_size; ++i)
Packit 6c4009
    for (struct aux_cache_entry *entry = aux_hash[i];
Packit 6c4009
	 entry != NULL; entry = entry->next)
Packit 6c4009
      if (entry->used)
Packit 6c4009
	{
Packit 6c4009
	  file_entries->libs[idx].id = entry->id;
Packit 6c4009
	  file_entries->libs[idx].flags = entry->flags;
Packit 6c4009
	  if (entry->soname == NULL)
Packit 6c4009
	    file_entries->libs[idx].soname = 0;
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      file_entries->libs[idx].soname = str_offset;
Packit 6c4009
Packit 6c4009
	      size_t len = strlen (entry->soname) + 1;
Packit 6c4009
	      str = mempcpy (str, entry->soname, len);
Packit 6c4009
	      str_offset += len;
Packit 6c4009
	    }
Packit 6c4009
	  file_entries->libs[idx].osversion = entry->osversion;
Packit 6c4009
	  file_entries->libs[idx++].pad = 0;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
  /* Write out auxiliary cache file.  */
Packit 6c4009
  /* Write auxiliary cache first to a temporary file and rename it later.  */
Packit 6c4009
Packit 6c4009
  char *temp_name = xmalloc (strlen (aux_cache_name) + 2);
Packit 6c4009
  sprintf (temp_name, "%s~", aux_cache_name);
Packit 6c4009
Packit 6c4009
  /* Check that directory exists and create if needed.  */
Packit 6c4009
  char *dir = strdupa (aux_cache_name);
Packit 6c4009
  dir = dirname (dir);
Packit 6c4009
Packit 6c4009
  struct stat64 st;
Packit 6c4009
  if (stat64 (dir, &st) < 0)
Packit 6c4009
    {
Packit 6c4009
      if (mkdir (dir, 0700) < 0)
Packit 6c4009
	goto out_fail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Create file.  */
Packit 6c4009
  int fd = open (temp_name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW,
Packit 6c4009
		 S_IRUSR|S_IWUSR);
Packit 6c4009
  if (fd < 0)
Packit 6c4009
    goto out_fail;
Packit 6c4009
Packit 6c4009
  if (write (fd, file_entries, file_entries_size + total_strlen)
Packit 6c4009
      != (ssize_t) (file_entries_size + total_strlen)
Packit 6c4009
      || fdatasync (fd) != 0
Packit 6c4009
      || close (fd) != 0)
Packit 6c4009
    {
Packit 6c4009
      unlink (temp_name);
Packit 6c4009
      goto out_fail;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Move temporary to its final location.  */
Packit 6c4009
  if (rename (temp_name, aux_cache_name))
Packit 6c4009
    unlink (temp_name);
Packit 6c4009
Packit 6c4009
out_fail:
Packit 6c4009
  /* Free allocated memory.  */
Packit 6c4009
  free (temp_name);
Packit 6c4009
  free (file_entries);
Packit 6c4009
}