elf_link_check_versioned_symbol

Alan Modra amodra@bigpond.net.au
Thu Jun 20 23:40:00 GMT 2002


Hi H.J.,
  I've tidied up one of your binutils patches that hasn't been
applied to sourceware, and I'd like you to check that the changes
are OK.  The main functional difference I made is that
elf_link_check_versioned_symbol now counts the number of matching
versioned symbols, as ld.so does.

HJ's original patch:
http://sources.redhat.com/ml/binutils/2001-05/msg00147.html

and testcase:
http://sources.redhat.com/ml/libc-alpha/2001-05/msg00128.html

Recent bug-report fixed by HJ's patch:
http://sources.redhat.com/ml/bug-binutils/2002-q2/msg00374.html

bfd/ChangeLog
2002-06-21  H.J. Lu  <hjl@gnu.org>

	* 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	21 Jun 2002 05:33:04 -0000
@@ -80,6 +80,8 @@ static size_t elf_link_sort_relocs
   PARAMS ((bfd *, struct bfd_link_info *, asection **));
 static boolean elf_section_ignore_discarded_relocs
   PARAMS ((asection *));
+static boolean elf_link_check_versioned_symbol
+  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 
 /* Given an ELF BFD, add symbols to the global hash table as
    appropriate.  */
@@ -2315,6 +2317,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:
@@ -6042,6 +6058,137 @@ 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;
+  int num_versions = 0;
+
+  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;
+
+      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 shoud
+		 have provided a definition for the undefined sym.  */
+	      abort ();
+	    }
+
+	  if ((iver.vs_vers & VERSYM_VERSION) >= 2)
+	    {
+	      /* We found one we can use.  */
+	      num_versions++;
+	    }
+	}
+
+      free (extversym);
+      free (buf);
+    }
+
+  return num_versions == 1;
+}
+
 /* 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 +6238,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