about summary refs log tree commit diff
path: root/elf/dl-lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-lookup.c')
-rw-r--r--elf/dl-lookup.c37
1 files changed, 28 insertions, 9 deletions
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 5245c628d2..8c99f499fd 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -77,6 +77,9 @@ do_lookup (const char *undef_name, unsigned long int hash,
       const char *strtab;
       const ElfW(Half) *verstab;
       ElfW(Symndx) symidx;
+      int num_versions = 0;
+      const ElfW(Sym) *sym;
+      const ElfW(Sym) *versioned_sym;
 
       map = list[i];
 
@@ -114,7 +117,7 @@ do_lookup (const char *undef_name, unsigned long int hash,
 	   symidx != STN_UNDEF;
 	   symidx = map->l_chain[symidx])
 	{
-	  const ElfW(Sym) *sym = &symtab[symidx];
+	  sym = &symtab[symidx];
 
 	  if (sym->st_value == 0 || /* No value.  */
 	      (elf_machine_lookup_noplt_p (reloc_type) /* Reject PLT entry.  */
@@ -134,13 +137,20 @@ do_lookup (const char *undef_name, unsigned long int hash,
 	    {
 	      /* No specific version is selected.  When the object
 		 file also does not define a version we have a match.
-		 Otherwise we only accept the default version, i.e.,
-		 the version which name is "".  */
+		 Otherwise we accept the default version, or in case
+		 there is only one version defined, this one version.  */
 	      if (verstab != NULL)
 		{
 		  ElfW(Half) ndx = verstab[symidx] & 0x7fff;
 		  if (ndx > 2) /* map->l_versions[ndx].hash != 0) */
-		    continue;
+		    {
+		      /* Don't accept hidden symbols.  */
+		      if ((verstab[symidx] & 0x8000) == 0
+			  && num_versions++ == 0)
+			/* No version so far.  */
+			versioned_sym = sym;
+		      continue;
+		    }
 		}
 	    }
 	  else
@@ -170,6 +180,19 @@ do_lookup (const char *undef_name, unsigned long int hash,
 		}
 	    }
 
+	  /* There cannot be another entry for this symbol so stop here.  */
+	  goto found_it;
+	}
+
+      /* If we have seem exactly one versioned symbol while we are
+	 looking for an unversioned symbol and the version is not the
+	 default version we still accept this symbol since there are
+	 no possible ambiguities.  */
+      sym = num_versions == 1 ? versioned_sym : NULL;
+
+      if (sym != NULL)
+	{
+	found_it:
 	  switch (ELFW(ST_BIND) (sym->st_info))
 	    {
 	    case STB_GLOBAL:
@@ -178,8 +201,7 @@ do_lookup (const char *undef_name, unsigned long int hash,
 	      result->m = map;
 	      return 1;
 	    case STB_WEAK:
-	      /* Weak definition.  Use this value if we don't find
-		 another.  */
+	      /* Weak definition.  Use this value if we don't find another.  */
 	      if (! result->s)
 		{
 		  result->s = sym;
@@ -190,9 +212,6 @@ do_lookup (const char *undef_name, unsigned long int hash,
 	      /* Local symbols are ignored.  */
 	      break;
 	    }
-
-	  /* There cannot be another entry for this symbol so stop here.  */
-	  break;
 	}
 
       /* If this current map is the one mentioned in the verneed entry