Blame elf/dl-version.c

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