Blame elf/dl-version.c

Packit Service 82fcde
/* Handle symbol and library versioning.
Packit Service 82fcde
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <elf.h>
Packit Service 82fcde
#include <errno.h>
Packit Service 82fcde
#include <libintl.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <ldsodefs.h>
Packit Service 82fcde
#include <_itoa.h>
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
Packit Service 82fcde
static inline struct link_map *
Packit Service 82fcde
__attribute ((always_inline))
Packit Service 82fcde
find_needed (const char *name, struct link_map *map)
Packit Service 82fcde
{
Packit Service 82fcde
  struct link_map *tmap;
Packit Service 82fcde
  unsigned int n;
Packit Service 82fcde
Packit Service 82fcde
  for (tmap = GL(dl_ns)[map->l_ns]._ns_loaded; tmap != NULL;
Packit Service 82fcde
       tmap = tmap->l_next)
Packit Service 82fcde
    if (_dl_name_match_p (name, tmap))
Packit Service 82fcde
      return tmap;
Packit Service 82fcde
Packit Service 82fcde
  /* The required object is not in the global scope, look to see if it is
Packit Service 82fcde
     a dependency of the current object.  */
Packit Service 82fcde
  for (n = 0; n < map->l_searchlist.r_nlist; n++)
Packit Service 82fcde
    if (_dl_name_match_p (name, map->l_searchlist.r_list[n]))
Packit Service 82fcde
      return map->l_searchlist.r_list[n];
Packit Service 82fcde
Packit Service 82fcde
  /* Should never happen.  */
Packit Service 82fcde
  return NULL;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
match_symbol (const char *name, Lmid_t ns, ElfW(Word) hash, const char *string,
Packit Service 82fcde
	      struct link_map *map, int verbose, int weak)
Packit Service 82fcde
{
Packit Service 82fcde
  const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit Service 82fcde
  ElfW(Addr) def_offset;
Packit Service 82fcde
  ElfW(Verdef) *def;
Packit Service 82fcde
  /* Initialize to make the compiler happy.  */
Packit Service 82fcde
  int result = 0;
Packit Service 82fcde
  struct dl_exception exception;
Packit Service 82fcde
Packit Service 82fcde
  /* Display information about what we are doing while debugging.  */
Packit Service 82fcde
  if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_VERSIONS))
Packit Service 82fcde
    _dl_debug_printf ("\
Packit Service 82fcde
checking for version `%s' in file %s [%lu] required by file %s [%lu]\n",
Packit Service 82fcde
		      string, DSO_FILENAME (map->l_name),
Packit Service 82fcde
		      map->l_ns, name, ns);
Packit Service 82fcde
Packit Service 82fcde
  if (__glibc_unlikely (map->l_info[VERSYMIDX (DT_VERDEF)] == NULL))
Packit Service 82fcde
    {
Packit Service 82fcde
      /* The file has no symbol versioning.  I.e., the dependent
Packit Service 82fcde
	 object was linked against another version of this file.  We
Packit Service 82fcde
	 only print a message if verbose output is requested.  */
Packit Service 82fcde
      if (verbose)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* XXX We cannot translate the messages.  */
Packit Service 82fcde
	  _dl_exception_create_format
Packit Service 82fcde
	    (&exception, DSO_FILENAME (map->l_name),
Packit Service 82fcde
	     "no version information available (required by %s)", name);
Packit Service 82fcde
	  goto call_cerror;
Packit Service 82fcde
	}
Packit Service 82fcde
      return 0;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  def_offset = map->l_info[VERSYMIDX (DT_VERDEF)]->d_un.d_ptr;
Packit Service 82fcde
  assert (def_offset != 0);
Packit Service 82fcde
Packit Service 82fcde
  def = (ElfW(Verdef) *) ((char *) map->l_addr + def_offset);
Packit Service 82fcde
  while (1)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Currently the version number of the definition entry is 1.
Packit Service 82fcde
	 Make sure all we see is this version.  */
Packit Service 82fcde
      if (__builtin_expect (def->vd_version, 1) != 1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  char buf[20];
Packit Service 82fcde
	  buf[sizeof (buf) - 1] = '\0';
Packit Service 82fcde
	  /* XXX We cannot translate the message.  */
Packit Service 82fcde
	  _dl_exception_create_format
Packit Service 82fcde
	    (&exception, DSO_FILENAME (map->l_name),
Packit Service 82fcde
	     "unsupported version %s of Verdef record",
Packit Service 82fcde
	     _itoa (def->vd_version, &buf[sizeof (buf) - 1], 10, 0));
Packit Service 82fcde
	  result = 1;
Packit Service 82fcde
	  goto call_cerror;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Compare the hash values.  */
Packit Service 82fcde
      if (hash == def->vd_hash)
Packit Service 82fcde
	{
Packit Service 82fcde
	  ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
Packit Service 82fcde
Packit Service 82fcde
	  /* To be safe, compare the string as well.  */
Packit Service 82fcde
	  if (__builtin_expect (strcmp (string, strtab + aux->vda_name), 0)
Packit Service 82fcde
	      == 0)
Packit Service 82fcde
	    /* Bingo!  */
Packit Service 82fcde
	    return 0;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* If no more definitions we failed to find what we want.  */
Packit Service 82fcde
      if (def->vd_next == 0)
Packit Service 82fcde
	break;
Packit Service 82fcde
Packit Service 82fcde
      /* Next definition.  */
Packit Service 82fcde
      def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* Symbol not found.  If it was a weak reference it is not fatal.  */
Packit Service 82fcde
  if (__glibc_likely (weak))
Packit Service 82fcde
    {
Packit Service 82fcde
      if (verbose)
Packit Service 82fcde
	{
Packit Service 82fcde
	  /* XXX We cannot translate the message.  */
Packit Service 82fcde
	  _dl_exception_create_format
Packit Service 82fcde
	    (&exception, DSO_FILENAME (map->l_name),
Packit Service 82fcde
	     "weak version `%s' not found (required by %s)", string, name);
Packit Service 82fcde
	  goto call_cerror;
Packit Service 82fcde
	}
Packit Service 82fcde
      return 0;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* XXX We cannot translate the message.  */
Packit Service 82fcde
  _dl_exception_create_format
Packit Service 82fcde
    (&exception, DSO_FILENAME (map->l_name),
Packit Service 82fcde
     "version `%s' not found (required by %s)", string, name);
Packit Service 82fcde
  result = 1;
Packit Service 82fcde
 call_cerror:
Packit Service 82fcde
  _dl_signal_cexception (0, &exception, N_("version lookup error"));
Packit Service 82fcde
  _dl_exception_free (&exception);
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
_dl_check_map_versions (struct link_map *map, int verbose, int trace_mode)
Packit Service 82fcde
{
Packit Service 82fcde
  int result = 0;
Packit Service 82fcde
  const char *strtab;
Packit Service 82fcde
  /* Pointer to section with needed versions.  */
Packit Service 82fcde
  ElfW(Dyn) *dyn;
Packit Service 82fcde
  /* Pointer to dynamic section with definitions.  */
Packit Service 82fcde
  ElfW(Dyn) *def;
Packit Service 82fcde
  /* We need to find out which is the highest version index used
Packit Service 82fcde
    in a dependecy.  */
Packit Service 82fcde
  unsigned int ndx_high = 0;
Packit Service 82fcde
  struct dl_exception exception;
Packit Service 82fcde
  /* Initialize to make the compiler happy.  */
Packit Service 82fcde
  int errval = 0;
Packit Service 82fcde
Packit Service 82fcde
  /* If we don't have a string table, we must be ok.  */
Packit Service 82fcde
  if (map->l_info[DT_STRTAB] == NULL)
Packit Service 82fcde
    return 0;
Packit Service 82fcde
  strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
Packit Service 82fcde
Packit Service 82fcde
  dyn = map->l_info[VERSYMIDX (DT_VERNEED)];
Packit Service 82fcde
  def = map->l_info[VERSYMIDX (DT_VERDEF)];
Packit Service 82fcde
Packit Service 82fcde
  if (dyn != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* This file requires special versions from its dependencies.  */
Packit Service 82fcde
      ElfW(Verneed) *ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
Packit Service 82fcde
Packit Service 82fcde
      /* Currently the version number of the needed entry is 1.
Packit Service 82fcde
	 Make sure all we see is this version.  */
Packit Service 82fcde
      if (__builtin_expect (ent->vn_version, 1) != 1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  char buf[20];
Packit Service 82fcde
	  buf[sizeof (buf) - 1] = '\0';
Packit Service 82fcde
	  /* XXX We cannot translate the message.  */
Packit Service 82fcde
	  _dl_exception_create_format
Packit Service 82fcde
	    (&exception, DSO_FILENAME (map->l_name),
Packit Service 82fcde
	     "unsupported version %s of Verneed record",
Packit Service 82fcde
	     _itoa (ent->vn_version, &buf[sizeof (buf) - 1], 10, 0));
Packit Service 82fcde
	call_error:
Packit Service 82fcde
	  _dl_signal_exception (errval, &exception, NULL);
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      while (1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  ElfW(Vernaux) *aux;
Packit Service 82fcde
	  struct link_map *needed = find_needed (strtab + ent->vn_file, map);
Packit Service 82fcde
Packit Service 82fcde
	  /* If NEEDED is NULL this means a dependency was not found
Packit Service 82fcde
	     and no stub entry was created.  This should never happen.  */
Packit Service 82fcde
	  assert (needed != NULL);
Packit Service 82fcde
Packit Service 82fcde
	  /* Make sure this is no stub we created because of a missing
Packit Service 82fcde
	     dependency.  */
Packit Service 82fcde
	  if (__builtin_expect (! trace_mode, 1)
Packit Service 82fcde
	      || ! __builtin_expect (needed->l_faked, 0))
Packit Service 82fcde
	    {
Packit Service 82fcde
	      /* NEEDED is the map for the file we need.  Now look for the
Packit Service 82fcde
		 dependency symbols.  */
Packit Service 82fcde
	      aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
Packit Service 82fcde
	      while (1)
Packit Service 82fcde
		{
Packit Service 82fcde
		  /* Match the symbol.  */
Packit Service 82fcde
		  result |= match_symbol (DSO_FILENAME (map->l_name),
Packit Service 82fcde
					  map->l_ns, aux->vna_hash,
Packit Service 82fcde
					  strtab + aux->vna_name,
Packit Service 82fcde
					  needed->l_real, verbose,
Packit Service 82fcde
					  aux->vna_flags & VER_FLG_WEAK);
Packit Service 82fcde
Packit Service 82fcde
		  /* Compare the version index.  */
Packit Service 82fcde
		  if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
Packit Service 82fcde
		    ndx_high = aux->vna_other & 0x7fff;
Packit Service 82fcde
Packit Service 82fcde
		  if (aux->vna_next == 0)
Packit Service 82fcde
		    /* No more symbols.  */
Packit Service 82fcde
		    break;
Packit Service 82fcde
Packit Service 82fcde
		  /* Next symbol.  */
Packit Service 82fcde
		  aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
Packit Service 82fcde
		}
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (ent->vn_next == 0)
Packit Service 82fcde
	    /* No more dependencies.  */
Packit Service 82fcde
	    break;
Packit Service 82fcde
Packit Service 82fcde
	  /* Next dependency.  */
Packit Service 82fcde
	  ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  /* We also must store the names of the defined versions.  Determine
Packit Service 82fcde
     the maximum index here as well.
Packit Service 82fcde
Packit Service 82fcde
     XXX We could avoid the loop by just taking the number of definitions
Packit Service 82fcde
     as an upper bound of new indeces.  */
Packit Service 82fcde
  if (def != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      ElfW(Verdef) *ent;
Packit Service 82fcde
      ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
Packit Service 82fcde
      while (1)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
Packit Service 82fcde
	    ndx_high = ent->vd_ndx & 0x7fff;
Packit Service 82fcde
Packit Service 82fcde
	  if (ent->vd_next == 0)
Packit Service 82fcde
	    /* No more definitions.  */
Packit Service 82fcde
	    break;
Packit Service 82fcde
Packit Service 82fcde
	  ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (ndx_high > 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Now we are ready to build the array with the version names
Packit Service 82fcde
	 which can be indexed by the version index in the VERSYM
Packit Service 82fcde
	 section.  */
Packit Service 82fcde
      map->l_versions = (struct r_found_version *)
Packit Service 82fcde
	calloc (ndx_high + 1, sizeof (*map->l_versions));
Packit Service 82fcde
      if (__glibc_unlikely (map->l_versions == NULL))
Packit Service 82fcde
	{
Packit Service 82fcde
	  _dl_exception_create
Packit Service 82fcde
	    (&exception, DSO_FILENAME (map->l_name),
Packit Service 82fcde
	     N_("cannot allocate version reference table"));
Packit Service 82fcde
	  errval = ENOMEM;
Packit Service 82fcde
	  goto call_error;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* Store the number of available symbols.  */
Packit Service 82fcde
      map->l_nversions = ndx_high + 1;
Packit Service 82fcde
Packit Service 82fcde
      /* Compute the pointer to the version symbols.  */
Packit Service 82fcde
      map->l_versyms = (void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
Packit Service 82fcde
Packit Service 82fcde
      if (dyn != NULL)
Packit Service 82fcde
	{
Packit Service 82fcde
	  ElfW(Verneed) *ent;
Packit Service 82fcde
	  ent = (ElfW(Verneed) *) (map->l_addr + dyn->d_un.d_ptr);
Packit Service 82fcde
	  while (1)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      ElfW(Vernaux) *aux;
Packit Service 82fcde
	      aux = (ElfW(Vernaux) *) ((char *) ent + ent->vn_aux);
Packit Service 82fcde
	      while (1)
Packit Service 82fcde
		{
Packit Service 82fcde
		  ElfW(Half) ndx = aux->vna_other & 0x7fff;
Packit Service 82fcde
		  /* In trace mode, dependencies may be missing.  */
Packit Service 82fcde
		  if (__glibc_likely (ndx < map->l_nversions))
Packit Service 82fcde
		    {
Packit Service 82fcde
		      map->l_versions[ndx].hash = aux->vna_hash;
Packit Service 82fcde
		      map->l_versions[ndx].hidden = aux->vna_other & 0x8000;
Packit Service 82fcde
		      map->l_versions[ndx].name = &strtab[aux->vna_name];
Packit Service 82fcde
		      map->l_versions[ndx].filename = &strtab[ent->vn_file];
Packit Service 82fcde
		    }
Packit Service 82fcde
Packit Service 82fcde
		  if (aux->vna_next == 0)
Packit Service 82fcde
		    /* No more symbols.  */
Packit Service 82fcde
		    break;
Packit Service 82fcde
Packit Service 82fcde
		  /* Advance to next symbol.  */
Packit Service 82fcde
		  aux = (ElfW(Vernaux) *) ((char *) aux + aux->vna_next);
Packit Service 82fcde
		}
Packit Service 82fcde
Packit Service 82fcde
	      if (ent->vn_next == 0)
Packit Service 82fcde
		/* No more dependencies.  */
Packit Service 82fcde
		break;
Packit Service 82fcde
Packit Service 82fcde
	      /* Advance to next dependency.  */
Packit Service 82fcde
	      ent = (ElfW(Verneed) *) ((char *) ent + ent->vn_next);
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* And insert the defined versions.  */
Packit Service 82fcde
      if (def != NULL)
Packit Service 82fcde
	{
Packit Service 82fcde
	  ElfW(Verdef) *ent;
Packit Service 82fcde
	  ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
Packit Service 82fcde
	  while (1)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      ElfW(Verdaux) *aux;
Packit Service 82fcde
	      aux = (ElfW(Verdaux) *) ((char *) ent + ent->vd_aux);
Packit Service 82fcde
Packit Service 82fcde
	      if ((ent->vd_flags & VER_FLG_BASE) == 0)
Packit Service 82fcde
		{
Packit Service 82fcde
		  /* The name of the base version should not be
Packit Service 82fcde
		     available for matching a versioned symbol.  */
Packit Service 82fcde
		  ElfW(Half) ndx = ent->vd_ndx & 0x7fff;
Packit Service 82fcde
		  map->l_versions[ndx].hash = ent->vd_hash;
Packit Service 82fcde
		  map->l_versions[ndx].name = &strtab[aux->vda_name];
Packit Service 82fcde
		  map->l_versions[ndx].filename = NULL;
Packit Service 82fcde
		}
Packit Service 82fcde
Packit Service 82fcde
	      if (ent->vd_next == 0)
Packit Service 82fcde
		/* No more definitions.  */
Packit Service 82fcde
		break;
Packit Service 82fcde
Packit Service 82fcde
	      ent = (ElfW(Verdef) *) ((char *) ent + ent->vd_next);
Packit Service 82fcde
	    }
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  return result;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
_dl_check_all_versions (struct link_map *map, int verbose, int trace_mode)
Packit Service 82fcde
{
Packit Service 82fcde
  struct link_map *l;
Packit Service 82fcde
  int result = 0;
Packit Service 82fcde
Packit Service 82fcde
  for (l = map; l != NULL; l = l->l_next)
Packit Service 82fcde
    result |= (! l->l_faked
Packit Service 82fcde
	       && _dl_check_map_versions (l, verbose, trace_mode));
Packit Service 82fcde
Packit Service 82fcde
  return result;
Packit Service 82fcde
}