Recursive shared library loading - Cannot open shared object file - c++

I am compiling an application which consists of several projects, that generate dynamic libraries (Shared Libraries on LINUX). Of course that different projects are linking to the others that I've compiled. I am using CodeBlocks 10 under Ubuntu, using GCC compiler.
Due to the fact that, according to the arguments specified by the user, different libraries shall be loaded, in my main application I am loading the appropriate library, according to the decision, with the following line:
dll = dlopen("my_library.so", RTLD_LAZY);
As specified in documentation, the dlopen loads libraries automatically If the library has dependencies on other shared libraries and the process is done recursively.
The problem is that right after my dlopen, I call dlerror() in order to understand what's going on and I get the following error:
../../../../gccDebug/os.so : Cannot open shared object file: No such
file or directory.
Just looking at the error, I completly understand it, because it is looking 2 folders below more than it should. The question is why?
What I mean is: I use relative paths to explicitly load Shared Libraries on the projects. On my Main Application, the Working Directory is ../../gccDebug.
I load, using dlopen, mylibrary.so, which explicitly loads (in project Options) ../../gccDebug/gui.so. This gui.so then also explicitly loads (in project options) ../../gccDebug/so.os
What it seems to me that is happening, is that he is appending the paths making that on the 3rd "iteration" he is looking for a path which is already searching too many folders back than it should. If the first recursive loading (gui.so) works just fine, why does the 2nd recursive loading (so.os) is giving a strange path??
What is wrong with the recursive loading of the shared libraries using dlopen function?

Each path should be relative to the library doing the loading, so for ../../gccDebug/gui.so to load something in the same directory it should load ./gui.so
The extra ../.. are because you've told something in ../../gccDebug to load something in ../../gccDebug_relative to itself_ which relative to your program's working directory is../../gccDebug/../../gccDebugi.e.../../../gccDebug`
Do that a few times for different libraries and you'll get the number of unwanted .. components you're seeing.
Are you sure that gui.so actually loaded? Could it be that mylibrary.so had copied the ../../gccDebug/os.so dependency from gui.so at link-time and so at run-time was trying to load that before loading gui.so?
Have you use ldd mylibrary.so to see what it tries to find? You can also use readelf -d mylibrary.so to see the contents of the dynamic section of the library.

Related

Run-time linker issue: "error while loading shared libraries"

I am working on a C++ project consisting of multiple similar programs. Each use a variety of headers and binaries, around 80. I managed to compile each program, and successfully run each but one of them, one named VerifyServer. When I try to run this, I get the below error:
./VerifyServer: error while loading shared libraries: libboost_system.so.1.57.0: cannot open shared object file: No such file or directory
However, here's the weird thing:
I was getting a very similar error when I tried running the other programs as well, but then I added
-Wl,-rpath=$(BOOST_LIB_HEADER_PATH)/stage/lib
to each of my compiler arguments, and it worked. I managed to run each programm, and even the file indicated in the error message of VerifyServer changed. Note that the variable BOOST_LIB_HEADER_PATH is correctly assigned to the path of my boost libraries.
Since my run time linker was failing to find this binary to correctly link it with my program, I tried checking which dependencies it was unable to find, so when I ran
ldd VerifyServer | grep boost (I use grep for simplicity)
I get this message:
libboost_thread.so.1.57.0 => /path/to/boost_1_57_0/stage/lib/libboost_thread.so.1.57.0 (0x00007f34c9b36000)
libboost_serialization.so.1.57.0 => /path/to/boost_1_57_0/stage/lib/libboost_serialization.so.1.57.0 (0x00007f34c9ad1000)
libboost_system.so.1.57.0 => not found
So, by now I assume that the system binary is not in the same directory as the others. When I manually check, I see that oddly enough, it is right there! So I decide to do the same thing on one of the programs that run without any problems, and I use
ldd IdentityProviderServer | grep boost
and I get the following message:
libboost_thread.so.1.57.0 => /path/to/boost_1_57_0/stage/lib/libboost_thread.so.1.57.0 (0x00007f97b6094000)
libboost_serialization.so.1.57.0 => /path/to/boost_1_57_0/stage/lib/libboost_serialization.so.1.57.0 (0x00007f97b602f000)
libboost_system.so.1.57.0 => /path/to/boost_1_57_0/stage/lib/libboost_system.so.1.57.0 (0x00007f97b602a000)
libboost_filesystem.so.1.57.0 => /path/to/boost_1_57_0/stage/lib/libboost_filesystem.so.1.57.0 (0x00007f97b600d000)
When I see that the system binary can be found in the same location as others, I just get confused. I don't know what the issue is here. Is there something I am missing about how the linker works?
Please note that I have to use the version 1.57.0 of boost here. I cannot update it to the current version, and I doubt my issue has anything to do with the version of boost.
This may be due to the linker unable to find transient dependencies. If you are directly linking against EG: libboost_thread, then the applications runtime linker will use the -rpath path that you defined when compiling.
HOWEVER, the libboost_thread may require ITS OWN library, libboost_system. The question is, should the libboost_thread library's runtime linker use your applications -rpath? or ignore it and use its own system search paths?? You must explicitly tell the runtime linker what it should do. Note, the default runtime linker search behavior changes depending on your compiler version. Google, RUNPATH vs RPATH and -Wl,--disable-new-dtags
I have answered this issue in another similar question: CMake project fails to find shared library
Although that user is using CMake to build their application, the underlying problem may be occurring here too.

Load dependent library with dlopen in case of custom library location

My program uses 'libone.so' object from certain vendor. It has custom location within a system. The program searches for this library location and then open it with dlopen. A problem appears when 'libone.so' do some stuff under the hood and starts to load 'libsecond.so'. I see with strace that dlopen tries to find 'libsecond.so' by standard paths defined for ldconfig, doesn't use the location of 'libone.so' as one of possible paths.
Is it possible to configure dlopen call within 'libone.so' so as it will load 'libsecond.so' from location of 'libone.so'? ('libsecond.so' located at the same path as 'libone.so')
ps. I can't affect on system configuration(ldconf paths) before program start. I need to resolve this problem within my program.
pss. If I add path to 'libone.so' to /etc/ld.so.conf.d/custom.conf then problem solved.
I think you have three options:
Create a script file (e.g. bash, Python, etc) that sets LD_LIBRARY_PATH before executing your binary program and make it executable.
Using -rpath linker option, add a fixed absolute path to the search paths which will be used by dynamic linker in addition to LD_LIBRARY_PATH.
dlopen with absolute paths from bottom up. i.e. first dlopen dependencies then dlopen the main library.
I would prefer the first option.

Best practice for C++ program with plugins in Linux

Can someone shed some light on what is the best practice for loading plugins into a C++ Linux program?
Say we have a program (editor) with a plugin (libsyntax.so). The config file for editor contains the path to the libsyntax.so library (plugin1=/opt/editor/gizmos/libsyntax.so). The editor reads the config then calls:
void* library = dlopen(path, RTLD_NOW|RTLD_GLOBAL);
MYFUN* function = (MYFUN*)dlsym(library, "function");
All is fine, stuff works.
Now let us assume that (libsyntax.so) depends on a helper library (libcolor.so). When we run readelf we get:
readelf -d libsyntax.so
Dynamic section at offset 0x12b6b48 contains 31 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libcolor.so]
...
However at this point the above dlopen() fails and the error is "No such file or directory". Using LD_DEBUG=all reveals that after libsyntax.so is loaded, messages are:
28664: file=libcolor.so [0]; needed by /home/.../libsyntax.so [0]
28664: find library=libcolor.so [0]; searching
28664: search cache=/etc/ld.so.cache
28664: search path=/lib64/tls/x86_64:/lib64/tls:...:/usr/lib64 (system search path)
28664: trying file=/lib64/tls/x86_64/libcolor.so
... and so on
The loader/linker is looking in standard places and, obviously, not finding my dependency. This can be easily taken care of by ldconfig or LD_LIBRARY_PATH but both solutions feel dirty.
Is there a clean way to load both the plugin and dependencies? How are you doing this?
A clean way to ensure dependencies are found, is to set the run-time search path (RPATH) during linking of the program and its plugins to a sensible location, where the dependencies can be found.
If the rpath was set for a binary (which you can see with readelf), the linker will use the additional paths listed there besides the default system locations, and LD_LIBRARY_PATH.
Additionally, there's a special variable, $ORIGIN, which is always resolved to the location of the current binary during runtime, and you can set paths relative to that!
For example, if your application's root path (that contains the main executable) is /opt/editor, the plugins are under /opt/editor/gizmos, and let's say you have another folder, /opt/editor/lib with additional dependencies, you might use the linker options:
# editor executable
-Wl,-rpath=$ORIGIN/lib
# plugins
-Wl,-rpath=$ORIGIN/../lib,-rpath=$ORIGIN
Persinally, I think that LD_LIBARY_PATH is your friend here. Just define, as the definition for the plugin interface, where libraries the plugin needs should be located, and make sure, in your program, to set the LD_LIBARY_PATH to that location.
Simply setting it before calling dlopen should be enough for the library to load. No other change is needed in the program, and no special linking of the plugin itself.

Force Qt5 to load SSL dlls from exe dir

Update Sorry, guys, I was incorrectly determined the problem. All is working, error was in "other program logic". Please delete or close the question.
Qt5 is designed to load libeay32.dll and ssleay32.dll on program start before any instruction in main() (because it is static).
(Details: it is located in qtbase\src\network\ssl\qsslsocket_openssl_symbols.cpp:
static QPair<QSystemLibrary*, QSystemLibrary*> loadOpenSslWin32()
)
Issue:
my program starts with not its exe dir as working directory
libeay32.dll and ssleay32.dll resides in its exe dir
user cannot install OpenSSL in system dir
user cannot change PATH variable
I cannot recompile Qt, i.e. ship program with static Qt compiled with openssl-linked
Qt loads searches dlls in this order (from qtbase\src\corelib\plugin\qsystemlibrary.cpp):
Application path.
System libraries path.
Trying all paths inside the PATH environment variable.
No. 1 is Application path, but in fact it does not search there.
My program:
int main()
{
// at this point Qt5 already checked and tried to load the DLLs
// so this:
ChangeCurrentWorkingDirectoryToExeDir(); // some function to change cwd to current exe dir
// does not work :-(
// ... other program logic ..
}
How to force Qt5 to reload OpenSSL DLLs after changing working directory?
May be someone already faced this problem...
Update Sorry, guys, I was incorrectly determined the problem. All is working, error was in "other program logic". Please delete or close the question.
QSystemLibrary::load is called with onlySystemDirectory = false for SSL, so QFileInfo(qAppFileName()).path() is the first place where the DLLs are searched. Search order:
application dir
system path (e.g. C:\Windows\System32)
all paths in PATH
I don't find documentation for that, but in our software, Qt finds SSL libeay32.dll and ssleay32.dll when they are in the same directory as the application .exe, given that
the .dll files are not in PATH
the .dll files are not in the working directory
no qt.conf exists
If you have a qt.conf, the default library value might apply, which is .\lib.
Library search orders are discussed on MSDN at Dynamic-Link Library Search Order.
There's a few ways to handle loading libraries, but it sounds like most of them won't apply.
Its not clear to me why you can call ChangeCurrentWorkingDirectoryToExeDir, but you can't call SetDllDirectory. I'm probably missing something obvious.
It seems like your last option is to create an Qt.exe.local file. This is called Dynamic-Link Library Redirection, and will cause the linker to load the DLL specified in the local file.

Unable to load shared library : libxerces.so

While running the application developed by other person, getting the following error
./appln: error while loading shared libraries: libxerces-c.so.28: cannot open shared object file: No such file or directory
And if I run ldd command:
# ldd appln
linux-gate.so.1 => (0x00e20000)
libdl.so.2 => /lib/libdl.so.2 (0x00a61000)
libxerces-c.so.28 => not found
I already have that libxerces-c.so.28 file in current folder. Please help me how to resolve that error
You need to put libxerces-c.so somewhere in the library path. Probably current folder is not searched for libraries. Try putting it in /usr/local/lib
Evidently "the current folder" isn't in the run time search path used by your executable. I'm assuming that you are on linux (linux-gate.so.1).
You need to ensure that "the current" directory is under the search path. You can do this at link time by using the -rpath option to the linker (-R is also accepted) or -Wl,-rpath,<dir> if you are invoking the linker through the compiler front end. This embeds a search path to be used at run time into your program.
Consider how you want the program to be installed, though. There's no point in adding a path that is specific to your development environment. You might want to consider using $ORIGIN or a $ORIGIN relative path which tells the run time linker to look for shared objects in the location containing (or relative to) the executable. You should always avoid adding . to the run time search path; your program shouldn't behave differently depending on the current directory of the process which invokes it.
As a temporary measure you can set the environment variable LD_LIBRARY_PATH to override the embedded and system search paths but it is normally a bad idea to rely on LD_LIBRARY_PATH overrides for a final installation.
By default .so files are NOT being searched in the current folder (they should be in /usr/lib, etc).
To add the current directory for .so lookup use:
LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH ./appln
When adding new "local system" libraries (e.g. in /usr/local/lib/) you better add that directory (/usr/local/lib/) once in your /etc/ld.so.conf and you should run ldconfig to update the linker cache (every time you add something inside /usr/local/lib/)
See ldconfig(8), ld.so(8), ldd(1), dlopen(3)
If you want your own libraries, set LD_LIBRARY_PATH to a directory containing them (e.g. $HOME/lib/ and to standard directories, e.g.
export LD_LIBRARY_PATH=$HOME/lib:/usr/lib:/usr/local/lib
in your ~/.bashrc (but I dislike that confusing practice, and prefer to manage my /usr/local/lib/).
You could also use some -Wl,-rpath argument but I really dislike that also.
Read also Program Library HowTo and Drepper's paper: How To Write Shared Libraries