elf_link_check_versioned_symbol

Alan Modra amodra@bigpond.net.au
Sat Jun 22 03:41:00 GMT 2002


On Fri, Jun 21, 2002 at 09:40:23AM -0700, H . J . Lu wrote:
> You missed:
> 
>               if ((verstab[symidx] & 0x7fff) 
>                   >= ((flags & DL_LOOKUP_RETURN_NEWEST) ? 2 : 3)) 
> 
> An unversioned reference can be resolved by the oldest version. The
> rest of code is ok although I prefer to keep the error message instead
> of abort () just in case. If you want, I can submit a new working
> patch.

The code you use in your binutils doesn't check for multiple
VERSYM_HIDDEN symbols.  That seems wrong to me, since as far as I can
see, ld.so will do so in certain cases.

I didn't exactly miss that code in elf/do-lookup.h;  I looked at it
and chose to use the comparison against 2.  On looking again, I see
that ld.so doesn't set DL_LOOKUP_RETURN_NEWEST, so the right thing to
do is return success on finding a version 2 sym, and count syms with
versions >= 3.  That will make both your testcases work, and match
ld.so behaviour.

As far as error message vs abort, I prefer the abort as it should
never happen with any user input.  ie. we're checking ld internal
consistency.  The other thing you mentioned privately, the change
in elf_link_loaded_list order was deliberate.  It a) is simpler
code, and b) the search order doesn't matter.

bfd/ChangeLog
	* elf-bfd.h (elf_link_loaded_list): New structure.
	(elf_link_hash_table): Add "loaded".
	* elf.c (_bfd_elf_link_hash_table_init): Initialize "loaded".
	* elflink.h (elf_link_check_versioned_symbol): New function.
	(elf_link_output_extsym): Call elf_link_check_versioned_symbol.

Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.79
diff -u -p -r1.79 elf-bfd.h
--- bfd/elf-bfd.h	7 Jun 2002 14:57:10 -0000	1.79
+++ bfd/elf-bfd.h	21 Jun 2002 05:28:38 -0000
@@ -223,6 +223,12 @@ struct elf_link_local_dynamic_entry
   Elf_Internal_Sym isym;
 };
 
+struct elf_link_loaded_list
+{
+  struct elf_link_loaded_list *next;
+  bfd *abfd;
+};
+
 enum elf_link_info_type
 {
   ELF_INFO_TYPE_NONE,
@@ -297,6 +303,9 @@ struct elf_link_hash_table
 
   /* Cached start, size and alignment of PT_TLS segment.  */
   struct elf_link_tls_segment *tls_segment;
+
+  /* A linked list of BFD's loaded in the link.  */
+  struct elf_link_loaded_list *loaded;
 };
 
 /* Look up an entry in an ELF linker hash table.  */
Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.147
diff -u -p -r1.147 elf.c
--- bfd/elf.c	9 Jun 2002 03:08:54 -0000	1.147
+++ bfd/elf.c	21 Jun 2002 05:28:43 -0000
@@ -1430,6 +1430,7 @@ _bfd_elf_link_hash_table_init (table, ab
   table->bucketcount = 0;
   table->needed = NULL;
   table->runpath = NULL;
+  table->loaded = NULL;
   table->hgot = NULL;
   table->stab_info = NULL;
   table->merge_info = NULL;
Index: bfd/elflink.h
===================================================================
RCS file: /cvs/src/src/bfd/elflink.h,v
retrieving revision 1.167
diff -u -p -r1.167 elflink.h
--- bfd/elflink.h	19 Jun 2002 10:07:36 -0000	1.167
+++ bfd/elflink.h	22 Jun 2002 10:20:29 -0000
@@ -2315,6 +2315,20 @@ elf_link_add_object_symbols (abfd, info)
 	  }
     }
 
+  if (is_elf_hash_table (info))
+    {
+      /* Add this bfd to the loaded list.  */
+      struct elf_link_loaded_list *n;
+
+      n = ((struct elf_link_loaded_list *)
+	   bfd_alloc (abfd, sizeof (struct elf_link_loaded_list)));
+      if (n == NULL)
+	goto error_return;
+      n->abfd = abfd;
+      n->next = hash_table->loaded;
+      hash_table->loaded = n;
+    }
+
   return true;
 
  error_return:
@@ -4536,6 +4550,8 @@ static boolean elf_link_output_extsym
   PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_link_sec_merge_syms
   PARAMS ((struct elf_link_hash_entry *, PTR));
+static boolean elf_link_check_versioned_symbol
+  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 static boolean elf_link_input_bfd
   PARAMS ((struct elf_final_link_info *, bfd *));
 static boolean elf_reloc_link_order
@@ -6042,6 +6058,147 @@ elf_link_sec_merge_syms (h, data)
   return true;
 }
 
+/* For DSOs loaded in via a DT_NEEDED entry, emulate ld.so in
+   allowing an unsatisfied unversioned symbol in the DSO to match a
+   versioned symbol that would normally require an explicit version,
+   if we find exactly one matching versioned symbol.  */
+
+static boolean
+elf_link_check_versioned_symbol (info, h)
+     struct bfd_link_info *info;
+     struct elf_link_hash_entry *h;
+{
+  bfd *undef_bfd = h->root.u.undef.abfd;
+  struct elf_link_loaded_list *loaded;
+  Elf_External_Sym *buf;
+  Elf_External_Versym *extversym;
+
+  if ((undef_bfd->flags & DYNAMIC) == 0
+      || info->hash->creator->flavour != bfd_target_elf_flavour
+      || elf_dt_soname (h->root.u.undef.abfd) == NULL)
+    return false;
+
+  for (loaded = elf_hash_table (info)->loaded;
+       loaded != NULL;
+       loaded = loaded->next)
+    {
+      bfd *input;
+      Elf_Internal_Shdr *hdr;
+      bfd_size_type symcount;
+      bfd_size_type extsymcount;
+      bfd_size_type extsymoff;
+      Elf_Internal_Shdr *versymhdr;
+      Elf_External_Versym *ever;
+      Elf_External_Sym *esym;
+      Elf_External_Sym *esymend;
+      bfd_size_type count;
+      file_ptr pos;
+      int num_versions = 0;
+
+      input = loaded->abfd;
+
+      /* We check each DSO for a possible hidden versioned definition.  */
+      if (input == undef_bfd
+	  || (input->flags & DYNAMIC) == 0
+	  || elf_dynversym (input) == 0)
+	continue;
+
+      hdr = &elf_tdata (input)->dynsymtab_hdr;
+
+      symcount = hdr->sh_size / sizeof (Elf_External_Sym);
+      if (elf_bad_symtab (input))
+	{
+	  extsymcount = symcount;
+	  extsymoff = 0;
+	}
+      else
+	{
+	  extsymcount = symcount - hdr->sh_info;
+	  extsymoff = hdr->sh_info;
+	}
+
+      if (extsymcount == 0)
+	continue;
+
+      count = extsymcount * sizeof (Elf_External_Sym);
+      buf = (Elf_External_Sym *) bfd_malloc (count);
+      if (buf == NULL)
+	return false;
+
+      /* Read in the symbol table.  */
+      pos = hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym);
+      if (bfd_seek (input, pos, SEEK_SET) != 0
+	  || bfd_bread ((PTR) buf, count, input) != count)
+	goto error_ret;
+
+      /* Read in any version definitions.  */
+      versymhdr = &elf_tdata (input)->dynversym_hdr;
+      extversym = (Elf_External_Versym *) bfd_malloc (versymhdr->sh_size);
+      if (extversym == NULL)
+	goto error_ret;
+
+      if (bfd_seek (input, versymhdr->sh_offset, SEEK_SET) != 0
+	  || (bfd_bread ((PTR) extversym, versymhdr->sh_size, input)
+	      != versymhdr->sh_size))
+	{
+	  free (extversym);
+	error_ret:
+	  free (buf);
+	  return false;
+	}
+
+      ever = extversym + extsymoff;
+      esymend = buf + extsymcount;
+      for (esym = buf; esym < esymend; esym++, ever++)
+	{
+	  const char *name;
+	  Elf_Internal_Sym sym;
+	  Elf_Internal_Versym iver;
+
+	  elf_swap_symbol_in (input, esym, NULL, &sym);
+	  if (ELF_ST_BIND (sym.st_info) == STB_LOCAL
+	      || sym.st_shndx == SHN_UNDEF)
+	    continue;
+
+	  name = bfd_elf_string_from_elf_section (input,
+						  hdr->sh_link,
+						  sym.st_name);
+	  if (strcmp (name, h->root.root.string) != 0)
+	    continue;
+
+	  _bfd_elf_swap_versym_in (input, ever, &iver);
+
+	  if ((iver.vs_vers & VERSYM_HIDDEN) == 0)
+	    {
+	      /* If we have a non-hidden versioned sym, then it should
+		 have provided a definition for the undefined sym.  */
+	      abort ();
+	    }
+
+	  if ((iver.vs_vers & VERSYM_VERSION) == 2)
+	    {
+	      /* This is the oldest (default) sym.  We can use it.  */
+	      free (extversym);
+	      free (buf);
+	      return true;
+	    }
+
+	  if ((iver.vs_vers & VERSYM_VERSION) > 2)
+	    {
+	      /* We found one we can use if there's only one such sym.  */
+	      num_versions++;
+	    }
+	}
+
+      free (extversym);
+      free (buf);
+      if (num_versions == 1)
+	return true;
+    }
+
+  return false;
+}
+
 /* Add an external symbol to the symbol table.  This is called from
    the hash table traversal routine.  When generating a shared object,
    we go through the symbol table twice.  The first time we output
@@ -6091,7 +6248,8 @@ elf_link_output_extsym (h, data)
       && ! finfo->info->shared
       && h->root.type == bfd_link_hash_undefined
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0
-      && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
+      && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0
+      && ! elf_link_check_versioned_symbol (finfo->info, h))
     {
       if (! ((*finfo->info->callbacks->undefined_symbol)
 	     (finfo->info, h->root.root.string, h->root.u.undef.abfd,

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre



More information about the Binutils mailing list