Package: libstdc++6 Version: 4.9.2-10 Severity: normal Where libstdc++ is dynamically loaded, in some cases it prevents an unrelated dynamic library that was loaded earlier from being unloaded. It also, incidentally, can't itself be unloaded, but that's much less of a problem. When I say that a library can't be unloaded, I mean that dlclose() indicates success, but the library actually remains mapped. I've been probing this with a test program, which just dlopens a sequence of libraries and then dlcloses them in reverse sequence, looking at what's actually mapped at each point:
$ cat try_dl.c #include <stdio.h> #include <dlfcn.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char **argv) { setbuf(stdout, NULL); if(argc <= 1) exit(2); argv++; argc--; void **handles = malloc(sizeof(void*) * argc); if(!handles) exit(1); char maps_cmd[100]; sprintf(maps_cmd, "sed -n 's:.* /: /:p' /proc/%ld/maps | sort -u", (long)getpid()); system(maps_cmd); for(int i = 0; i != argc; i++) { handles[i] = dlopen(argv[i], RTLD_LAZY); printf("dlopen(\"%s\", RTLD_LAZY) = %p\n", argv[i], handles[i]); system(maps_cmd); if(!handles[i]) exit(1); } for(int i = argc; i--; ) { int ret = dlclose(handles[i]); printf("dlclose(%p /*\"%s\"*/) = %d\n", handles[i], argv[i], ret); system(maps_cmd); if(ret) exit(1); } exit(0); } $ gcc -std=c99 try_dl.c -ldl -o try_dl $ ./try_dl /usr/lib/x86_64-linux-gnu/libperl.so.5.20 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so dlopen("/usr/lib/x86_64-linux-gnu/libperl.so.5.20", RTLD_LAZY) = 0x1761070 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libcrypt-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so /lib/x86_64-linux-gnu/libm-2.19.so /lib/x86_64-linux-gnu/libpthread-2.19.so /usr/lib/x86_64-linux-gnu/libperl.so.5.20.2 dlopen("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", RTLD_LAZY) = 0x1762ae0 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libcrypt-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libm-2.19.so /lib/x86_64-linux-gnu/libpthread-2.19.so /usr/lib/x86_64-linux-gnu/libperl.so.5.20.2 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20 dlclose(0x1762ae0 /*"/usr/lib/x86_64-linux-gnu/libstdc++.so.6"*/) = 0 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libcrypt-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libm-2.19.so /lib/x86_64-linux-gnu/libpthread-2.19.so /usr/lib/x86_64-linux-gnu/libperl.so.5.20.2 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20 dlclose(0x1761070 /*"/usr/lib/x86_64-linux-gnu/libperl.so.5.20"*/) = 0 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libcrypt-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libm-2.19.so /lib/x86_64-linux-gnu/libpthread-2.19.so /usr/lib/x86_64-linux-gnu/libperl.so.5.20.2 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20 Observe that both libperl and libstdc++ remain loaded after the dlcloses, along with all their dependencies. But if libperl is loaded without libstdc++, it unloads as one would expect: $ ./try_dl /usr/lib/x86_64-linux-gnu/libperl.so.5.20 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so dlopen("/usr/lib/x86_64-linux-gnu/libperl.so.5.20", RTLD_LAZY) = 0xd48070 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libcrypt-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so /lib/x86_64-linux-gnu/libm-2.19.so /lib/x86_64-linux-gnu/libpthread-2.19.so /usr/lib/x86_64-linux-gnu/libperl.so.5.20.2 dlclose(0xd48070 /*"/usr/lib/x86_64-linux-gnu/libperl.so.5.20"*/) = 0 /home/zefram/tmp/try_dl /lib/x86_64-linux-gnu/ld-2.19.so /lib/x86_64-linux-gnu/libc-2.19.so /lib/x86_64-linux-gnu/libdl-2.19.so /lib/x86_64-linux-gnu/libpthread-2.19.so In this case libpthread, loaded as one of libperl's dependencies, won't unload, but this doesn't seem to be a problem. I'll spare you the full try_dl traces for other combinations; here's what I've learned from experimenting with it. Most shared libraries unload as they ought to. libpthread and libstdc++ share the unusual feature seen above that once loaded (either directly or as a dependency) they won't unload, and this seems to be something inherent in those libraries. A handful of other libraries also won't unload, but have either libpthread or libstdc++ as a dependency, so maybe their stickiness is caused by those two: for example librt (depending on libpthread) and libjack (depending on libstdc++). Not all libraries depending on those two get this behaviour, however. Sometimes, where a library depends on libstdc++ and others, the main library will unload, but libstdc++ and some other dependency will not. For example, libxatracker depends on libstdc++ and libffi (and some others), and can itself be unloaded along with some of its dependencies, but leaves both libstdc++ and libffi loaded. If libffi and libstdc++ are loaded without libxatracker, libstdc++ stays loaded but libffi unloads OK. libstdc++ alone, not libpthread, has this behaviour of keeping loaded a library that does not depend on it. Only a small number of libraries are affected by this: libperl (as seen above), libtcl, and libtk. The stickiness only occurs if libstdc++ is loaded after the victim. If libstdc++ is loaded first, then the victim is able to unload (though libstdc++ itself still won't unload). On a system I have access to with older packages, including libstdc++ 4.4.3, libstdc++ does not have the effect of preventing library unloading, either of itself or of other libraries. I do see libpthread and librt failing to unload, but not all of libpthread's dependents that have that behaviour on the newer system. The real problem that I run into due to this unloading problem is concerned with running Perl code under Apache, for which I have custom builds of both. Apache likes to process its configuration twice in each run, unloading all of its modules between runs. When mod_perl is shut down, it tries to unload everything that Perl loaded. It so happens that one of the Perl XS modules incorporates some C++ code and so brings in libstdc++ as a dependency. libperl, having been loaded as a dependency of mod_perl, ought to unload when Apache unloads mod_perl, but due to this problem with libstdc++ it does not. In the absence of unloading, the second time through the configuration has stale values in Perl global variables, and the process quickly crashes when Perl follows a hook pointing at a module that was successfully unloaded. I need libperl to unload properly in order to get a fresh set of its global variables. I have a potential workaround of modifying Apache to explicitly load libstdc++ during startup, because (as noted above) having it loaded first seems to prevent it interfering with the unloading of other modules. -zefram -- To UNSUBSCRIBE, email to debian-gcc-requ...@lists.debian.org with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org Archive: https://lists.debian.org/20150604145523.gk23...@fysh.org