Search code examples
cpthreadsshared-librariesforkglibc

pthread_atfork fails to compile when declaring -nostartfiles


I have an old application that uses the pthread_atfork function from the pthread library to register a child hook. I'm in the process of moving the application to a newer build environment which uses glibc 2.14.90. pthread_atfork appears to have been essentially removed from the pthread library back in 2002, and one of the shared libraries we create will no longer compile.

Here is the code in question:

void
_init(void)
{
    static int first = 1;
    if ( first ) {
        pthread_atfork(NULL,NULL,_init);
        first = 0;
    }
    {
        pthread_t tid;
        pthread_create(&tid,0,startUp,0);
    }
    return;
}

The pthread_atfork call registers _init as a child hook, to be run in the child process every time there's a fork. Init just creates a new thread to execute startUp which is an initialization function that sets up stuff like files, mutexes, reads in initialization parameters, etc...

Since pthread_atfork is hidden in glibc 2.14, is there a another way to register the same kind of hook so that init gets run at fork? I would prefer to use the standard lib without any modification, to preserve portability of the build environment.

glibc 2.14 source code is here, for those interested (pthread_atfork files are under the nptl directory):

https://sourceware.org/git/?p=glibc.git;a=tree;h=d1e550124ee36b8b62af8984e4b829622a60f725;hb=356f8bc660a154a07b03da7c536831da5c8f74fe

Compilation error I receive:

gcc -g -nostartfiles -o mto.so -shared -I mto.c -lnsl -lresolv -lrt -lm -lpthread -ldl

/usr/bin/ld: /usr/lib/libpthread_nonshared.a(pthread_atfork.oS): relocation R_386_GOTOFF against undefined hidden symbol `__dso_handle' can not be used when making a shared object

$ gcc -v

Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.6.3/lto-wrapper Target: i686-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux Thread model: posix gcc version 4.6.3 20120306 (Red Hat 4.6.3-2) (GCC)

$ ldd --version

ldd (GNU libc) 2.14.90 Copyright (C) 2011 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Written by Roland McGrath and Ulrich Drepper.

$ nm /usr/lib/libpthread.a|grep -C 1 atfork
00008150 W __pread64
000060b0 T __pthread_atfork
00002ab0 T __pthread_attr_destroy
--
00007ba0 W __recvmsg
         U __register_atfork
00008c10 T __res_state
--
00008150 W pread64
000060b0 T pthread_atfork
00002ab0 T pthread_attr_destroy

.

$ nm /usr/lib/libpthread_nonshared.a

pthread_atfork.oS:
         U _GLOBAL_OFFSET_TABLE_
         w __dso_handle
00000000 T __i686.get_pc_thunk.bx
00000000 T __pthread_atfork
         U __register_atfork
00000000 T pthread_atfork

Thanks for your time.


Solution

  • Your problem is that the outer wrapper function pthread_atfork seems to have been moved to libpthread_nonshared.a so that it can identify the library calling it, presumably so that the installed handlers can be removed if/when the library is unloaded, and it achieves this by referencing the __dso_handle symbol which is defined in the start files, which you are intentionally omitting. You can't do this. Omitting start files is only valid for some low-level purposes; it's not compatible with linking any part of the standard library.

    Apparently the reason you're using -nostartfiles is to be able to write your own _init function that will run when the library is loaded, since without -nostartfiles it causes a linker error due to a clash with the same-name function in crti.o. Unfortunately this is the wrong fix.

    Redefining _init to run code at library load time is an unsupported hack that was deprecated a long time ago. The correct way to run code at library load time is to apply __attribute__((__constructor__)) to the function you want to run. Give the function a name that won't clash with anything, or just make it static so you don't have to worry about its name clashing.