Unload a shared library

I used ccall to run a function in a shared library. Now it’s loaded, and I’d like to unload it (say, to change something in the source and rebuild). Libdl.dlcose looked like it might be what I need, but it takes a handle, and I don’t have one. I tried to get one, but it didn’t help me close the library. Can you unload a library loaded in this way?

x = Libdl.dlopen("name_of_my_library")
Libdl.dlclose(x) # returns false
filter(x->contains(x, "name_of_my_library"), Libdl.dllist()) # Still shows my library as loaded.

On unix-like platforms, dlopen is reference-counted and the handle is not deleted until the reference count reaches zero. From a quick look, I think jl_dlopen doesn’t check handle existence before calling the system dlopen (which will increment and return if the library was already opened in the current process). It’s possible – and likely – that there are multiple calls and a reference count greater than 1 if you are using BinDeps.

For diagnostic purposes, you can also call dlerror (jl_dlerror exists internally but is not exported). unsafe_string(ccall(:dlerror, Cstring, ())) should work.

Thanks @ihnorton. If I understand you correctly, then this is why my method isn’t unloading the library. I think we’re still looking for a way to unload it after a ccall then, yes?

If you were calling

ccall((:my_fcn, "./my_lib.so"), ...)

but needed to close the library when done, then do this instead:

lib = Libdl.dlopen("./my_lib.so")
sym = Libdl.dlsym(lib, :my_fcn)
ccall(sym, ...) # Remaining arguments are the same.
Libdl.dlclose(lib)

More on the GitHub issue: https://github.com/JuliaLang/julia/issues/23459

1 Like