This is the mail archive of the libc-alpha@sources.redhat.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

NEW PATCH: Handle undefined symbol in DSO from DT_NEEDED


On Wed, May 09, 2001 at 11:36:37AM +0200, Jakub Jelinek wrote:
> On Tue, May 08, 2001 at 09:30:54PM -0700, H . J . Lu wrote:
> > On Tue, May 08, 2001 at 09:25:42PM -0700, Ulrich Drepper wrote:
> > > "H . J . Lu" <hjl@lucon.org> writes:
> > > 
> > > > glibc 2.2.3 changes atexit from default to hidden. DSOs with
> > > > unversioned references to atexit fail to link if they are loaded
> > > > in via a DT_NEEDED entry. This patch makes it non-fatal. I will
> > > > check it in if I don't hear any objections by Thursday.
> > > 
> > > Why should the linker ignore bugs?  Don't work around problems users
> > > introduced.  Make them fix their code.
> > > 
> > 
> > DSOs compiled against glibc 2.0 may have unversioned references to
> > atexit. I won't blame people for compiling DSOs against glibc 2.0.
> 
> Are you sure it does not work or is it just a guess?
> I've just compiled a program using atexit on a glibc 2.0.7 system (RHL 5.2),
> plus a program using shared library which uses atexit on the same box, moved
> them to glibc 2.2.3 box (both the program and DSO have unversioned atexit
> reference, glibc has atexit@GLIBC_2.0 only) and they work very well.
> Anyway, if you show me a testcase which would not work, then I should put

I have a testcase. I will post it later.

> 
> atexit into /lib/libNoVersion-2.2.3.so, it should not
> bloat things elsewhere. libNoVersion is needed to run glibc 2.0 programs
> anyway (at least for the vast majority of binary only apps).

It is a link-time problem, not a run-time problem. BTW, glibc doesn't
get it right. It works only by accident. I have sent in a testcase:

http://sources.redhat.com/ml/libc-alpha/2001-05/msg00074.html

and a patch

http://sources.redhat.com/ml/libc-alpha/2001-05/msg00031.html

Here is a new patch for ld. For undefined symbol in a DSO, the dynamic
linker binds it to a hidden definition if and only if there is only
one hidden definition. This patch will make ld to do the same, but 
only for DSO from DT_NEEDED. Otherwise, it is a real error.

If there is no objecttion, I will check it in by Friday.

H.J.
-----
2001-05-09  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 the
	 "loaded" field to NULL.

	* elflink.h (elf_link_check_versioned_symbol): New function.
	Return true if there is one and only one hidden definition
	for an undefined symbol.

	* elflink.h (elf_link_output_extsym): For undefined symbol in
	DSO from DT_NEEDED, Call elf_link_check_versioned_symbol to
	check if there is one and only one hidden definition.

Index: bfd/elf-bfd.h
===================================================================
RCS file: /work/cvs/gnu/binutils/bfd/elf-bfd.h,v
retrieving revision 1.8
diff -u -p -r1.8 elf-bfd.h
--- bfd/elf-bfd.h	2001/04/13 18:47:21	1.8
+++ bfd/elf-bfd.h	2001/05/09 22:29:05
@@ -213,6 +213,12 @@ struct elf_link_local_dynamic_entry
   Elf_Internal_Sym isym;
 };
 
+struct elf_link_loaded_list
+{
+  struct elf_link_loaded_list *next;
+  bfd *abfd;
+};
+
 /* ELF linker hash table.  */
 
 struct elf_link_hash_table
@@ -248,6 +254,8 @@ struct elf_link_hash_table
   /* A linked list of DT_RPATH/DT_RUNPATH names found in dynamic
      objects included in the link.  */
   struct bfd_link_needed_list *runpath;
+  /* 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: /work/cvs/gnu/binutils/bfd/elf.c,v
retrieving revision 1.51
diff -u -p -r1.51 elf.c
--- bfd/elf.c	2001/04/27 21:05:00	1.51
+++ bfd/elf.c	2001/05/09 22:29:05
@@ -1037,6 +1037,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: /work/cvs/gnu/binutils/bfd/elflink.h,v
retrieving revision 1.58
diff -u -p -r1.58 elflink.h
--- bfd/elflink.h	2001/05/03 06:45:26	1.58
+++ bfd/elflink.h	2001/05/09 22:29:05
@@ -60,7 +60,139 @@ static boolean elf_link_size_reloc_secti
 static void elf_link_adjust_relocs
   PARAMS ((bfd *, Elf_Internal_Shdr *, unsigned int,
 	   struct elf_link_hash_entry **));
+static boolean elf_link_check_versioned_symbol
+  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
 
+static boolean
+elf_link_check_versioned_symbol (info, h)
+     struct bfd_link_info *info;
+     struct elf_link_hash_entry *h;
+{
+  const char *name;
+  int num_versions = 0;
+  bfd *undef_bfd = h->root.u.undef.abfd;
+  struct elf_link_loaded_list *loaded;
+  bfd *input;
+  Elf_Internal_Shdr *hdr;
+  size_t symcount;
+  size_t extsymcount;
+  size_t extsymoff;
+  Elf_External_Sym *buf = NULL;
+  Elf_Internal_Shdr *versymhdr;
+  Elf_External_Versym *extversym = NULL;
+  Elf_External_Versym *ever;
+  Elf_Internal_Versym iver;
+  Elf_External_Sym *esym;
+  Elf_External_Sym *esymend;
+
+  if (info->hash->creator->flavour != bfd_target_elf_flavour)
+    return false;
+
+  for (loaded = elf_hash_table (info)->loaded;
+       loaded != NULL && num_versions == 0;
+       loaded = loaded->next)
+    {
+      input = loaded->abfd;
+
+      /* We check each DSO for a possible versioned difinition.  */
+      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;
+
+      buf = ((Elf_External_Sym *)
+	     bfd_malloc (extsymcount * sizeof (Elf_External_Sym)));
+      if (buf == NULL)
+	goto no_memory;
+
+      /* Read in the symbol table. */
+      if (bfd_seek (input,
+		     hdr->sh_offset
+		     + extsymoff * sizeof (Elf_External_Sym),
+		      SEEK_SET) != 0
+	 || (bfd_read ((PTR) buf, sizeof (Elf_External_Sym),
+		       extsymcount, input)
+	     != extsymcount * sizeof (Elf_External_Sym)))
+	goto error_return;
+
+      /* Read in any version definitions.  */
+      versymhdr = &elf_tdata (input)->dynversym_hdr;
+      extversym =
+	(Elf_External_Versym *) bfd_malloc (versymhdr->sh_size);
+      if (extversym == NULL)
+	goto no_memory;
+
+      if (bfd_seek (input, versymhdr->sh_offset, SEEK_SET) != 0
+	  || (bfd_read ((PTR) extversym, 1, versymhdr->sh_size, input)
+	      != versymhdr->sh_size))
+	goto error_return;
+
+      ever = extversym + extsymoff;
+      esymend = buf + extsymcount;
+      for (esym = buf; esym < esymend; esym++, ever++)
+	{
+	  Elf_Internal_Sym sym;
+
+	  elf_swap_symbol_in (input, esym, &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);
+
+	  /* It is defined here and we still get an undefined
+	     symbol.  */
+	  if ((iver.vs_vers & VERSYM_HIDDEN) == 0)
+	    {
+	      (*_bfd_error_handler)
+		(_("%s: defined in %s"), name,
+		 bfd_get_filename (input));
+	      bfd_set_error (bfd_error_bad_value);
+	      goto error_return;
+	    }
+	  /* We found one. */
+	  num_versions++;
+	}
+
+      free (buf);
+      free (extversym);
+    }
+
+  return num_versions == 1;
+
+no_memory:
+  bfd_set_error (bfd_error_no_memory);
+error_return:
+  if (buf != NULL)
+    free (buf);
+  if (extversym != NULL)
+    free (extversym);
+  return false;
+}
+
 /* Given an ELF BFD, add symbols to the global hash table as
    appropriate.  */
 
@@ -2191,6 +2322,22 @@ elf_link_add_object_symbols (abfd, info)
 	  goto error_return;
     }
 
+  {
+    /* We add this to the loaded list.  */
+    struct elf_link_loaded_list *n, **pn;
+
+    n = ((struct elf_link_loaded_list *)
+	 bfd_alloc (abfd, sizeof (struct elf_link_loaded_list)));
+    if (n == NULL)
+      goto error_return;
+    n->next = NULL;
+    n->abfd = abfd;
+    for (pn = &elf_hash_table (info)->loaded; *pn != NULL;
+	 pn = &(*pn)->next)
+      ;
+    *pn = n;
+  }
+
   return true;
 
  error_return:
@@ -5171,9 +5316,22 @@ elf_link_output_extsym (h, data)
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
     {
-      if (! ((*finfo->info->callbacks->undefined_symbol)
-	     (finfo->info, h->root.root.string, h->root.u.undef.abfd,
-	      (asection *) NULL, 0, true)))
+      /* If elf_dt_soname (h->root.u.undef.abfd) != NULL, that means
+	 this DSO is loaded in via a DT_NEEDED entry. In this case,
+	 we do some extra check to see if there is a suitable
+	 definition. */
+      boolean def;
+
+      if (elf_dt_soname (h->root.u.undef.abfd) != NULL)
+	def = elf_link_check_versioned_symbol (finfo->info, h);
+      else
+	def = false; 
+
+      if (!def
+	  && ! ((*finfo->info->callbacks->undefined_symbol)
+		  (finfo->info, h->root.root.string,
+		   h->root.u.undef.abfd, (asection *) NULL,
+		   0, true)))
 	{
 	  eoinfo->failed = true;
 	  return false;


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]