Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sun, 19 Aug 2012 20:48:03 -0400
From: Rich Felker <dalias@...ifal.cx>
To: musl@...ts.openwall.com
Subject: Re: ldso: dlclose.

On Sun, Aug 19, 2012 at 06:26:45PM +0200, musl wrote:
> Hi,
> 
> I noticed that the dlclose function is not implemented (always return 0).
> It might be a problem in my case cause I use dlfuncs to implement a plugin system and when I unload a plugin, the memory
> is not released.
> 
> Do you plan to implement this function ?

It is implemented. Per POSIX:

    The use of dlclose() reflects a statement of intent on the part of
    the process, but does not create any requirement upon the
    implementation, such as removal of the code or symbols referenced
    by handle.

Source:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlclose.html

To elaborate, removing a DSO from a process's address space is highly
non-trivial and error-prone. If you've spent any time reading glibc's
bug tracker, you'll find they've had a lot of nasty bugs stemming from
dlclose doing the wrong things. I don't want to recreate that kind of
bugginess. The requirements get even worse once we implement TLS.

At first, I thought it might be feasible to just support unloading of
DOSs that were loaded non-global, since there's no way their symbols
have been used to resolve references elsewhere in the program. However
there are still major ugly issues that seemingly can't be handled
correctly. Suppose for instance myplugin.so is part of your
application and intended only to be loaded/unloaded with
dlopen/dlclose, never linked directly, but it depends on libfoo.so
which was designed to be linked directly. Both will get loaded
non-globally when dlopen is called on myplugin.so, but libfoo.so might
do things that are incompatible with later being unloaded -- for
example, registering an atexit function. You can try to solve this
issue with __cxa_atexit and DSO handles but that only covers a special
case. The library might instead have registered its atexit function
through code in a third library, or it might not even be directly
registering an atexit function but instead a cleanup function that
some other library will call directly during its own cleanup routines,
or even registered pointers to functions or data within libfoo.so with
an interface in another library that's not doing to be unloaded at the
same time.

What it comes down to is that it's never safe to unload a library that
was not designed to be unloadable. As such, the maximum possible
unloading we could ever safely support is unloading only the dlopen'd
library itself and never any of its dependencies; but even that's not
entirely safe since some programs will directly dlopen libraries that
were not designed to be loaded and unloaded.

Also, note that all of the above is purely the fundamental issues that
make unloading a bad idea; I haven't even touched on all the things
that are easy to get wrong in the implementation, leading to race
conditions and crashes in the dynamic linker itself.

In summary, I don't see any way to make a correct implementation where
dlclose actually unloads stuff. If you have ideas for how to do it,
please explain; I'm not against doing it, but only if it can be done
correctly.

> BTW I think there is a bug in the '__funcs_on_exit' (atexit.c) : only the first non null function of each function pool
> is called.

I'll take a look...

> Something like this should do the trick.
> 
> void __funcs_on_exit()
> {
>     int i;
>     void (*func)(void *), *arg;
>     LOCK(lock);
>     for (; head; head=head->next) {
>         for (i=COUNT-1; i>=0 && !head->f[i]; i--);
>         if (i<0) continue;
>         for (; i>=0; i--) {
>             func = head->f[i];
>             arg = head->a[i];
>             head->f[i] = 0;
>             UNLOCK(lock);
>             func(arg);
>             LOCK(lock);
>         }
>     }
> }

You're right. Thanks for the catch! I've fixed it slightly differently
(avoiding two separate loops over the array).

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.