Subject: Re: libpthread and static constructor order
To: None <tech-userlevel@netbsd.org>
From: J.T. Conklin <jtc@acorntoolworks.com>
List: tech-userlevel
Date: 11/03/2007 10:33:48
Joerg Sonnenberger <joerg@britannica.bec.de> writes:
> On Fri, Nov 02, 2007 at 06:57:56AM -0700, J.T. Conklin wrote:
>> > On Thu, Nov 01, 2007 at 08:53:53PM -0700, J.T. Conklin wrote:
>> >> I'm running into this problem now with ACE / TAO, which has static
>> >> ctors which end up using pthread's thread specific storage.
>> >
>> > In that case the library has to explicitly link against libpthread. ELF
>> > constructors are run in dependency order and if you can build the
>> > library with -z defs, you should be on the save side.
>> 
>> Hmm... This is something I tried yesterday without much success.
>
> Note that what I said above is how it *should* behave. From reading
> ld.elf_so I am not sure whether it is actually doing that. Can you check
> in which order the DT_NEEDED entries are in the binary? I think the part
> about resorting the order list is not done at all...

Since ACE / TAO are huge, I wrote a small unit test program to verify
NetBSD's behavior (see the enclosed shar file). 

To simplify things, I used gcc's __constructor__ attribute instead of
C++.  There are two shared libraries, libfoo and libbar.  libfoo has a
constructor that writes "foo\n" to standard error; while libbar's ctor 
writes "bar\n".  libfoo has no dependencies, while libbar depends on 
libfoo. 

I've linked a "null" main() program with these shared libraries.  For
"foobar", I've linked with -lfoo -lbar; for "barfoo", I've linked with
-lbar -lfoo.  

I've read:
http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#init_fini

and my understanding is the output should be "foo\nbar\n" regardless
of the link order specified when compiling the main program, as libbar
depends on libfoo. But "foobar" (which links with -lfoo -lbar), prints
"bar\nfoo\n"., indicating that libbar is initialized before libfoo.

So while Thor's right that "libfool" (heh) is wrong in suppressing the
libpthread dependency, it appears that NetBSD ld.elf_so also needs
help.

    --jtc


# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	bar.c
#	foo.c
#	main.c
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
XCFLAGS=-O2
XLDFLAGS=-L.
X
Xall:	barfoo foobar
X
Xclean:
X	rm -f barfoo foobar *.o *.so
X
Xfoobar: libfoo.so libbar.so
X
Xbarfoo: libfoo.so libbar.so main.o
X	$(CC) $(LDFLAGS) -o $@ main.o -lbar -lfoo
X
Xfoobar: libfoo.so libbar.so main.o
X	$(CC) $(LDFLAGS) -o $@ main.o -lfoo -lbar
X
Xmain.o: main.c
X	$(CC) $(CFLAGS) -o $@ -c main.c
X
Xfoo.o:	foo.c
X	$(CC) $(CFLAGS) -o $@ -fPIC -c foo.c
X
Xbar.o:	bar.c
X	$(CC) -o $@ -fPIC -c bar.c
X
Xlibfoo.so: foo.o
X	$(CC) $(LDFLAGS) -o $@ -shared foo.o
X
Xlibbar.so: bar.o
X	$(CC) $(LDFLAGS) -o $@ -shared bar.o -lfoo
END-of-Makefile
echo x - bar.c
sed 's/^X//' >bar.c << 'END-of-bar.c'
X#include <fcntl.h>
X
Xvoid bar_ctor() __attribute__((__constructor__));
X
Xvoid
Xbar_ctor()
X{
X   write(2, "bar\n", 4);
X}
END-of-bar.c
echo x - foo.c
sed 's/^X//' >foo.c << 'END-of-foo.c'
X#include <fcntl.h>
X
Xvoid foo_ctor() __attribute__((__constructor__));
X
Xvoid
Xfoo_ctor()
X{
X   write(2, "foo\n", 4);
X}
END-of-foo.c
echo x - main.c
sed 's/^X//' >main.c << 'END-of-main.c'
Xint
Xmain()
X{
X   return 0;
X}
END-of-main.c
exit


-- 
J.T. Conklin