Is It Safe to Call Dlclose(Null)

Passing null to dlclose()

If it is not documented, it is most likely not (or at "best" undefined).
Actually a simple test could verify that for your platform:

Compiling this

#include <dlfcn.h>
int main()
{
dlclose(0);
}

Already yields the following compiler warning (gcc):

test.c: In function ‘main’:
test.c:5:2: warning: null argument where non-null required (argument 1) [-Wnonnull]
5 | dlclose(0);
| ^~~~~~~
/usr/bin/ld: /tmp/ccYF9oqz.o: in function `main':

An running it, crashes as expected with a segmentation fault.

Why you want to do or need to know this in the first place is the question.
It should be trivial to do a null-check before calling dlclose or provide
a wrapper that does it, if otherwise inconvenient.

Situation of handle after dlclose()/FreeLibrary() fails

Once you call dlcose the handle is no longer valid for your use:

http://pubs.opengroup.org/onlinepubs/007904975/functions/dlclose.html

Once an object has been closed using dlclose() an application should assume that its symbols are no longer available to dlsym().

It is quite possible that dlclose() does nothing. Because other libraries also have handles to the library. If the close fails then it usually means somebody else is using it. BUT that does not mean you can use it again via your handle (that is dead).

An error usually indicates your handle is invalid.

How to safely update a running dynamic library?

The dlclose() which you mark as possibly unsafe is, in fact, definitely unsafe. Any functions from the module currently active in other threads will have the floor pulled out from underneath them: their references to static data and other functions in the module will become dangling pointers.

So I'd say that you're better working out how to have two active versions of your module. You can't do that by just calling dlopen with the same path, because it caches open handles and it will just return the currently open handle with an incremented reference count. Instead, you can probably do the following:

  • Compile your modules into files with an included version number, and then symlink the official module filename to the latest version. (That's the way most .so files are generated on a typical Unix system.)

  • When you want to open a module, first use readlink(2) to find the currently-linked module version. Then open that path.

(I haven't actually tried that but I think it will work, at least on Unix-like systems.)

I'd suggest trying to avoid RTLD_GLOBAL, if possible. In general, dlcloseing a module opened with RTLD_GLOBAL is risky, at least; the dlclose might orphan resolved symbols used by other dynamically loaded modules. (And if no other module is going to use the symbols exported by the module you might dlclose, then RTLD_GLOBAL was never necessary.) I'm not convinced that RTLD_LAZY is a good idea, either.

Finally, you'll have to come up with some way to know when it is possible to dlclose old modules. You can't do it until you are certain that no thread is currently calling a function from the obsolete module. You might want to consider putting a reference count into the module structure, and using a macro or wrapper to ensure that reference counts are incremented and decremented pre- and post-call. You'll also want to add some kind of mutex to the module structure in order to avoid race conditions.

dlsym returns NULL, even though the symbol exists

I don't think you can do that, dlsym works on exported symbols. Because you're doing dlsym on NULL (current image), even though the symbols are present in the executable ELF image, they're not exported (since it's not a shared library).

Why not call it directly and let the linker take care of it? There's no point in using dlsym to get symbols from the same image as your dlsym call. If your testing symbol was in a shared library that you either linked against or loaded using dlopen then you would be able to retrieve it.

I believe there's also a way of exporting symbols when building executables (-Wl,--export-dynamic as mentioned in a comment by Brandon) but I'm not sure why you'd want to do that.



Related Topics



Leave a reply



Submit