Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Binutils linker bug?

1 view
Skip to first unread message

Kars de Jong

unread,
Nov 8, 1999, 3:00:00 AM11/8/99
to
Hi,

After reporting this as a bug to glibc, some discussion, some more tests,
it was decided that this bug was actually in the binutils.

The bug was seen on all Linux glibc platforms (with at least glibc 2.1).
Binutils versions at least:
GNU ld version 2.9.5 (with BFD 2.9.5.0.7)
GNU ld version 2.9.5 (with BFD 2.9.5.0.19)
GNU ld version 2.9.1 (with BFD 2.9.1.0.24)
With egcs version 1.1.2, gcc version 2.95.2.

The problem: under certain conditions the dynamic linker of glibc would
fail when 'ldd'-ing a program, with the following error message:

bash$ ldd threadtest
BUG IN DYNAMIC LINKER ld.so: dl-version.c: 210: _dl_check_map_versions: Assertion `needed != ((void
*)0)' failed!
bash$

The problem was finding the exact 'certain conditions'. But I managed to get
a testcase now. Basically, it involves having a shared library that calls
pthread library functions, and a program that uses the shared library but
isn't linked against libpthread itself.

file threadlib.c:
------------------------------------------------------------------------------
#include <pthread.h>

pthread_key_t mykey;

int dummy(void) {
int spec;

pthread_key_create(&mykey, NULL);
pthread_setspecific(mykey, (void *)10);
spec = (int)pthread_getspecific(mykey);
return spec;
}
------------------------------------------------------------------------------

file threadtest.c:
------------------------------------------------------------------------------
#include <pthread.h>
#include <stdio.h>

extern int dummy(void);

pthread_t *me;

int main(int argc, char **argv) {
printf("spec = %d\n", dummy());
me = pthread_self();
return 0;
}
------------------------------------------------------------------------------

Commands:
bash$ gcc -fPIC -O2 -shared -o libthreadlib.so threadlib.c -lpthread
bash$ gcc -O2 -o threadtest threadtest.c -L. -lthreadlib

Now, assuming you don't have . in your LD_LIBRARY_PATH, 'ldd threadtest'
triggers the assertion in the dynamic linker.

As to the problem: Look at the output of 'readelf':

bash$ readelf -d -V threadtest
Dynamic segment at offset 0x6a8 contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libthreadlib.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]

<snip>

Version needs section '.gnu.version_r' contains 2 entries:
Addr: 0x00000000080483e4 Offset: 0x0003e4 Link to section: 5 (.dynstr)
000000: Version: 1 File: libpthread.so.0 Cnt: 1
0x0010: Name: GLIBC_2.0 Flags: none Version: 3
0x0020: Version: 1 File: libc.so.6 Cnt: 1
0x0030: Name: GLIBC_2.0 Flags: none Version: 2
bash$

It was decided that libpthread.so.0 shouldn't have been in the .gnu.version_r
section, because it is not in the NEEDED list of the program.

Note that this testcase doesn't entirely represent the situation: in the
cases I ran into the bug (JDK 1.2.1 and ToolTalk version 1.3) the main
program that used the lib didn't call any pthread functions itself!
But I couldn't get the testcase to pull in pthread symbols without adding the
pthread_self() call to the main program.

Kars.
--
------------------------------------------------------------------------------
Kars de Jong Signaalkamp rules the waves! Turrican@Discworld
--------======]**-----| jo...@cs.utwente.nl |-----**[======---------

Ian Lance Taylor

unread,
Nov 8, 1999, 3:00:00 AM11/8/99
to
Date: Mon, 8 Nov 1999 14:10:48 +0100
From: Kars de Jong <jo...@cs.utwente.nl>

The problem was finding the exact 'certain conditions'. But I managed to get
a testcase now.

Thanks for the bug report. Unfortunately, your testcase appears to
depend upon a particular version of glibc. At least, I don't see any
problem on my system.

Would it be possible for you, or someone, to find a testcase which is
independent of glibc? This would probably involve writing some
version scripts.

bash$ readelf -d -V threadtest
Dynamic segment at offset 0x6a8 contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libthreadlib.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]

<snip>

Version needs section '.gnu.version_r' contains 2 entries:
Addr: 0x00000000080483e4 Offset: 0x0003e4 Link to section: 5 (.dynstr)
000000: Version: 1 File: libpthread.so.0 Cnt: 1
0x0010: Name: GLIBC_2.0 Flags: none Version: 3
0x0020: Version: 1 File: libc.so.6 Cnt: 1
0x0030: Name: GLIBC_2.0 Flags: none Version: 2
bash$

It was decided that libpthread.so.0 shouldn't have been in the .gnu.version_r
section, because it is not in the NEEDED list of the program.

I think the interesting question here is which symbols in the dynamic
symbol table, if any, were defined in libpthread.so.0 at version
GLIBC_2.0.

The NEEDED entry was omitted because libpthread.so was only required
indirectly by libthreadlib.so. In other words, a different
libpthread.so would be acceptable, provided it defined pthread_self
somehow.

But although the NEEDED entry was omitted, the call to pthread_self
really does come from libpthread.so, and it really does have an
attached version number.

You are suggesting that the linker should omit libpthread.so from the
version reference section because there is no NEEDED entry. But what
about the symbol pthread_self? Should we omit the version information
for that symbol? Wouldn't that be incorrect?

In other words, it's true that the executable does not require
libpthread.so. It can work with any implementation of libthreadlib.so
which defines pthread_self.

However, it's also true that the executable was compiled to expect a
particular version of pthread_self. If libpthread.so changes the
version of pthread_self, then the executable should presumably
continue to call the older version.

On the other hand, if libthreadlib.so provides a wholly new
implementation of pthread_self, I'm not sure what should happen.

Perhaps the correct solution is for the dynamic linker to use the
version number of pthread_self, but only if pthread_self winds up
being taken from libpthread.so.

That may not be the correct fix. But you haven't suggested a complete
fix. You've suggested that the .gnu.version_r entry for libpthread.so
should be omitted, but you haven't explained what version information,
if any, should be stored for symbols from libpthread.so which are used
in the final link.

If you propose a complete fix, we can discuss whether it seems
correct.

Ian

Mark Kettenis

unread,
Nov 8, 1999, 3:00:00 AM11/8/99
to
[ For the people on binutils@sourceware: this is a followup on a
binutils bug report that was sent to bug-...@gnu.org. I added
binutils@sourceware to give it a bit more exposure. ]

jo...@cs.utwente.nl (Kars de Jong) writes:

> Note that this testcase doesn't entirely represent the situation: in the
> cases I ran into the bug (JDK 1.2.1 and ToolTalk version 1.3) the main
> program that used the lib didn't call any pthread functions itself!
> But I couldn't get the testcase to pull in pthread symbols without adding the
> pthread_self() call to the main program.

I think you get the same result if you use write() instead of
pthread_self() in your main program. libpthread overrides several
system calls that can block for a long time to make them cancellation
points. In this context the fact that `write' is weak in libc.so but
an ordinary symbol in libpthread.so.

To me, this indicates that Kars's problems might be more complicated
than they would seem at first sight.

I think that everybody agrees that the testcase Kars gave (with main
explicitly calling pthread_self()) is broken in the sense that he
should link his program explicitly against libpthread.so. Would it be
a good idea to let `ld' issue a warning if he doesn't link against
libpthread.so? Should `ld' avoid recording the version dependencies
in this case?

However, if the program references `write' instead of `pthread_self'
things are a little different. I think the desired behaviour here is
to record that the program needs the appropriate version of `write'
from libc.so instead of libpthread.so (which gets sucked in via an other
library we link against). Is this failing because `write' is weak in
libc.so and isn't in libpthread.so? Or would it be a problem anyhow,
that is, even when `write' is weak in libpthread.so too?

Mark

0 new messages