fgetws implementation.
[kopensolaris-gnu/glibc.git] / elf / dl-version.c
index 4382a4c..8d59fe2 100644 (file)
@@ -1,5 +1,5 @@
 /* Handle symbol and library versioning.
-   Copyright (C) 1997 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
 
 
 #include <elf.h>
 #include <errno.h>
-#include <link.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
-
-#include "../stdio-common/_itoa.h"
+#include <elf/ldsodefs.h>
+#include <stdio-common/_itoa.h>
 
+#include <assert.h>
 
-/* Set in rtld.c at startup.  */
-extern char **_dl_argv;
 
-#define VERSTAG(tag)   (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
+#ifndef VERSYMIDX
+# define VERSYMIDX(tag)        (DT_NUM + DT_PROCNUM + DT_VERSIONTAGIDX (tag))
+#endif
 
 
 #define make_string(string, rest...) \
@@ -46,20 +45,27 @@ extern char **_dl_argv;
                                                                              \
     cp = result = alloca (len);                                                      \
     for (cnt = 0; cnt < sizeof (all) / sizeof (all[0]); ++cnt)               \
-      cp = stpcpy (cp, all[cnt]);                                            \
+      cp = __stpcpy (cp, all[cnt]);                                          \
                                                                              \
     result;                                                                  \
   })
 
 
 static inline struct link_map *
-find_needed (struct link_map *map, const char *name)
+find_needed (const char *name, struct link_map *map)
 {
+  struct link_map *tmap;
   unsigned int n;
 
-  for (n = 0; n < map->l_nsearchlist; ++n)
-    if (_dl_does_name_match_p (name, map->l_searchlist[n]))
-      return map->l_searchlist[n];
+  for (tmap = _dl_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;
@@ -67,27 +73,37 @@ find_needed (struct link_map *map, const char *name)
 
 
 static int
+internal_function
 match_symbol (const char *name, ElfW(Word) hash, const char *string,
              struct link_map *map, int verbose, int weak)
 {
-  const char *strtab = (const char *) (map->l_addr
-                                      + map->l_info[DT_STRTAB]->d_un.d_ptr);
-  ElfW(Addr) def_offset = map->l_info[VERSTAG (DT_VERDEF)]->d_un.d_ptr;
+  const char *strtab = (const void *) map->l_info[DT_STRTAB]->d_un.d_ptr;
+  ElfW(Addr) def_offset;
   ElfW(Verdef) *def;
 
-  if (def_offset == 0)
+  /* Display information about what we are doing while debugging.  */
+  if (_dl_debug_versions)
+    _dl_debug_message (1, "checking for version `", string, "' in file ",
+                      map->l_name[0] ? map->l_name : _dl_argv[0],
+                      " required by file ", name, "\n", NULL);
+
+  if (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)
-       _dl_signal_error (0, map->l_name, make_string ("\
+       _dl_signal_cerror (0, map->l_name,
+                          make_string ("\
 no version information available (required by ",
-                                                      name, ")"));
+                                       name, ")"));
       return 0;
     }
 
-  def = (ElfW(Verdef) *) (map->l_addr + def_offset);
+  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.
@@ -128,34 +144,42 @@ no version information available (required by ",
   if (weak)
     {
       if (verbose)
-       _dl_signal_error (0, map->l_name,
-                         make_string ("weak version `", string,
-                                      "' not found (required by ", name,
-                                      ")"));
+       _dl_signal_cerror (0, map->l_name,
+                          make_string ("weak version `", string,
+                                       "' not found (required by ", name,
+                                       ")"));
       return 0;
     }
 
-  _dl_signal_error (0, map->l_name,
-                   make_string ("version `", string,
-                                "' not found (required by ", name, ")"));
+  _dl_signal_cerror (0, map->l_name,
+                    make_string ("version `", string,
+                                 "' not found (required by ", name, ")"));
   return 1;
 }
 
 
 int
+internal_function
 _dl_check_map_versions (struct link_map *map, int verbose)
 {
   int result = 0;
-  const char *strtab = (const char *) (map->l_addr
-                                      + map->l_info[DT_STRTAB]->d_un.d_ptr);
+  const char *strtab;
   /* Pointer to section with needed versions.  */
-  ElfW(Dyn) *dyn = map->l_info[VERSTAG (DT_VERNEED)];
+  ElfW(Dyn) *dyn;
   /* Pointer to dynamic section with definitions.  */
-  ElfW(Dyn) *def = map->l_info[VERSTAG (DT_VERDEF)];
+  ElfW(Dyn) *def;
   /* We need to find out which is the highest version index used
     in a dependecy.  */
   unsigned int ndx_high = 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 *) map->l_info[DT_STRTAB]->d_un.d_ptr;
+
+  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.  */
@@ -179,7 +203,7 @@ _dl_check_map_versions (struct link_map *map, int verbose)
       while (1)
        {
          ElfW(Vernaux) *aux;
-         struct link_map *needed = find_needed (map, strtab + ent->vn_file);
+         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.  */
@@ -199,7 +223,7 @@ _dl_check_map_versions (struct link_map *map, int verbose)
                                      aux->vna_flags & VER_FLG_WEAK);
 
              /* Compare the version index.  */
-             if ((aux->vna_other & 0x7fff) > ndx_high)
+             if ((unsigned int) (aux->vna_other & 0x7fff) > ndx_high)
                ndx_high = aux->vna_other & 0x7fff;
 
              if (aux->vna_next == 0)
@@ -227,10 +251,10 @@ _dl_check_map_versions (struct link_map *map, int verbose)
   if (def != NULL)
     {
       ElfW(Verdef) *ent;
-      ent = (ElfW(Verdef)  *) (map->l_addr + def->d_un.d_ptr);
+      ent = (ElfW(Verdef) *) (map->l_addr + def->d_un.d_ptr);
       while (1)
        {
-         if ((ent->vd_ndx & 0x7fff) > ndx_high)
+         if ((unsigned int) (ent->vd_ndx & 0x7fff) > ndx_high)
            ndx_high = ent->vd_ndx & 0x7fff;
 
          if (ent->vd_next == 0)
@@ -246,13 +270,12 @@ _dl_check_map_versions (struct link_map *map, int verbose)
       /* 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 = (hash_name_pair*) malloc ((ndx_high + 1)
-                                                 * sizeof (hash_name_pair));
-      memset (map->l_versions, '\0', (ndx_high + 1) * sizeof (hash_name_pair));
+      map->l_versions = (struct r_found_version *)
+       calloc (ndx_high + 1, sizeof (*map->l_versions));
       if (map->l_versions == NULL)
        {
          _dl_signal_error (ENOMEM, (*map->l_name ? map->l_name : _dl_argv[0]),
-                           "cannot allocate version name table");
+                           "cannot allocate version reference table");
          result = 1;
        }
       else
@@ -260,6 +283,10 @@ _dl_check_map_versions (struct link_map *map, int verbose)
          /* Store the number of available symbols.  */
          map->l_nversions = ndx_high + 1;
 
+         /* Compute the pointer to the version symbols.  */
+         map->l_versyms =
+           (void *) map->l_info[VERSYMIDX (DT_VERSYM)]->d_un.d_ptr;
+
          if (dyn != NULL)
            {
              ElfW(Verneed) *ent;
@@ -272,7 +299,9 @@ _dl_check_map_versions (struct link_map *map, int verbose)
                    {
                      ElfW(Half) ndx = aux->vna_other & 0x7fff;
                      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.  */
@@ -308,6 +337,7 @@ _dl_check_map_versions (struct link_map *map, int verbose)
                      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)
@@ -325,13 +355,14 @@ _dl_check_map_versions (struct link_map *map, int verbose)
 
 
 int
+internal_function
 _dl_check_all_versions (struct link_map *map, int verbose)
 {
   struct link_map *l;
   int result = 0;
 
   for (l = map; l != NULL; l = l->l_next)
-    result |= _dl_check_map_versions (l, verbose);
+    result |= l->l_opencount != 0 && _dl_check_map_versions (l, verbose);
 
   return result;
 }