NEW PATCH: Handle undefined symbol in DSO from DT_NEEDED
H . J . Lu
hjl@lucon.org
Wed May 9 15:53:00 GMT 2001
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;
More information about the Libc-alpha
mailing list