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