Assume that no other executable linked against a shared library
I assume you have a program that was linked via (e.g.):
cc -o mypgm mypgm.o -lshlib
Upon execution, once the ELF interpreter has loaded
libshlib.so and executed the constructor(s), the library is never loaded again. Side note: To find your interpreter do:
readelf -a mypgm | grep interpreter:
If the program receives a signal (e.g.
SIGUSR1), the signal is either caught by a signal handler (assuming
sigaction has been called to set one up), or the default action is taken (which is [IIRC] program termination for
SIGUSR1). This does not cause the library to be reloaded.
There is no other action that can cause the library to be reloaded either. Only the destructor(s) will be called upon program exit (e.g.
main returns or
exit is called).
Even manually calling the destructors has no effect because the constructors and destructors are independent. (e.g. The constructor could do
able = malloc(...) and the destructor could do
free(able). But, the destructor could do
free(baker) instead.). Calling a destructor does not "reset" a constructor.
To get the "reload" effect, the library would need to be dynamically loaded/unloaded via
dlopen/dlsym/dlclose. That is, the link command would be:
cc -o mypgm mypgm.o
mypgm would [at some point] call
dlopen("libshlib.so") (and the constructor(s) would be called). When [and if]
libshlib.so will be unloaded (and destructor(s) called).
mypgm then called
dlopen("libshlib.so") a second time, the contructors would be called [again].
Note that calling
dlclosedoes not necessarily unload the library or call destructors.
I just checked the code [in
glibc]. The library has a refcount. The library will be unloaded if the refcount is 1 upon entry to
dlclose, which should be the case for
libshlib.so above with
dlopen [as nobody else bumps it up].
In other words, to force the "desired" behavior nothing else should refer to
libshlib via an
-lshlib. Not the program or any other
.so. This lays the groundwork.
Note that if
glibc, but so did the program, unloading
libshlib will bump down the
glibc refcount, but
glibc will remain because its refcount is [still] >0.
There are conditions where the library can't be unloaded (in fact, these conditions are much more common then conditions when the library can be unloaded).
Again, this is dependent upon the refcount and [possibly] some state. When the library is loaded from a "static" linkage (vs.
dlopen), the refcount gets an extra increment, so it won't get yanked.
The code also handles the case where a constructor calls
dlopen on its own library.
For a given
libA, if it needs
libB, B's refcount gets upped/downed by A's load/unload.
If the library is not unloaded, then it's not well defined whether destructors will run, and whether the subsequent dlopen will run constructors again
The whole point of using
dlopen this way for
libshlib is to guarantee the loading at
dlopen and unloading at
dlclose [along with constructor/destructor action]. This will be true if there is no static reference to it or cyclic dependency, which was the starting criteria.
The part about "as nobody else bumps it up" is way too simplistic.
Don't confuse prose with substance.
As mentioned above: This will be true if there is no static reference to it or cyclic dependency.
This means that only the executable/object that does the
shlib refers to a symbol in
And, this is only via
dlsym. Otherwise, it's a static reference (i.e. in the object's symbol table as UNDEF)].
And, no shared library that
shlib drags in refers to a symbol defined in
shlib [the cyclic dependency].
Look at all the places where DF_1_NODELETE is set during symbol resolution.
Yes, I did look.
DF_1_NODELETE is set in only the following places. None of them apply to this situation [or most
RTLD_NODELETE, which we can avoid.
WEAK, etc.) that is type 10 (
mallocfailure when adding a dependency [from a _different object]. This code doesn't even execute for the case herein.
And, OP's usage aside, there are legitimate reasons for
dlopen/dlclose to work as I've set up/described.
See my answer here: Is it possible to perform munmap based on information in /proc/self/maps?
There, OP needed to have a nonstop program that could run for months/years (e.g. a hi-reliabilty, mission-critical application). If an updated version of one of its shared libraries was installed [via a package manager, etc.], the program had to dynamically, on-the-fly, without a re-exec, be able to load the newer version.