Is it possible to get location of dependent libraries from within code - c++

I want to load some dependent libraries from my program with dlopen function. Is it possible to know actual location of these libraries?
eg, ldd shows all dependent libraries with paths in system. How does it work? Is it possible to get paths to correspondent libraries I need to load with dlopen via some call from my C++ code?

From man dlopen one can read:
The function dlopen() loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the dynamic library. If filename is NULL, then the returned handle is for the main program. If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname. Otherwise, the dynamic linker searches for the library as follows (see ld.so(8) for further details):
(ELF only) If the executable file for the calling program contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag, then the directories listed in the DT_RPATH tag are searched.
If, at the time that the program was started, the environment variable LD_LIBRARY_PATH was defined to contain a colon-separated list of directories, then these are searched. (As a security measure this variable is ignored for set-user-ID and set-group-ID programs.)
(ELF only) If the executable file for the calling program contains a DT_RUNPATH tag, then the directories listed in that tag are searched.
The cache file /etc/ld.so.cache (maintained by ldconfig(8)) is checked to see whether it contains an entry for filename.
The directories /lib and /usr/lib are searched (in that order).
So, if the desired library is "installed", a simple dlopen("foobar.so", flag) will do.

Related

CMake retrieve output path

In my Application the user can click on a "Build" button. This will invoke a file dialogue where the user selects the folder with the CmakeCache. After that I'll invoke the cmake --build command with system(command) and the executeable will be built.
Now I'd like to know the path where the executeable was built. I need to copy files into that path.
I know that the file located at /CMakeCacheFolder/projectname/projectname.dir/Release/projectname.log contains the compiler log with the output path in the last line. But is there some other way?
Output directory of the executable/library target tgt can be obtained with generator expression
$<TARGET_FILE_DIR:tgt>
Because this is a generator expression, it can be used only in limited cases. (CMake documents every command and parameter, for which a generator expression can be used).
If you know, that output directory for the target is set via assigning CMAKE_RUNTIME_OUTPUT_DIRECTORY variable, then you may read either this variable or the target's property RUNTIME_OUTPUT_DIRECTORY. Unlike to the generator expressions, the variable's and the property's values can be used everywhere.
But note, that in case of multi-configuration generators (like Visual Studio), configuration name is appended to the variable's (or property's) value for obtain real output directory.
Not a full answer but too much for a comment:
You can define the output directories for executables, shared objects, and libraries for a project as follows:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
And as you determine where to build you now have a common output directory under a well known path.
The directories could also be given on a configuration basis (Debug/Release/RelWithDebInfo/...) as follows
CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG
CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE
CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO
Hope that helps you further.

Using -L and -l flags vs giving library file as input

What's the difference between writing:
g++ test.cc -L/my/dir/ -lname
and
g++ test.cc /my/dir/libname.so ?
Are both correct?
The things that I can think of:
First one is cross platform, g++ from MINGW will look for DLL's
In the second one we explicitly specify we want dynamic linking
The difference is that
g++ test.cc -L/my/dir/ -lname
may succeed even if /my/dir/libname.so does not exist:
It can find libname.so from a system library directory (usually /lib, /usr/lib).
It can find libname.a, and do static linking.
Furthermore, it allows you to link to multiple libraries from the same directory without repeating the parent directory.
The first method looks into /my/dir directory for the library file at link time. However only a short library name libname.so (or whatever it finds according to OS) is written into the image. When the program starts, it will look for the library in some library path, i.e. LD_LIBRARY_PATH on linux. Now you can relocate the executable and the library into different new places and set a correct search path after relocation.
The second method writes the full path of the library into the image: /my/dir/libname.so. When the program starts, it will look for this particular path to get the library. This makes the image non-relocatable, unless the same library path exists in the new environment. The effect is similar to the '-rpath' method.

Link with self-compiled static libraries with cmake

I try to write some tests for project. If I need some project files I write include_directories statement and all will be included. In case of need of some manually compiled static libraries I try to set target_link_libraries.
If I set absolute path then all links ok, but for task I need another way to link another libraries, because the relative path to libraries gives undefined references.
In this case everything works fine:
target_link_libraries(ConsoleDumperTest GTest::GTest GTest::Main PocoFoundation PocoUtil PocoNet ${YAML_LIBRARIES})
target_link_libraries(ConsoleDumperTest /home/yrusinov/projects/build-fleetd-2-Desktop-dbg/protocols/libprotocols.a)
target_link_libraries(ConsoleDumperTest /home/yrusinov/projects/build-fleetd-2-Desktop-dbg/consumers/libconsumers.a)
target_link_libraries(ConsoleDumperTest /home/yrusinov/projects/build-fleetd-2-Desktop-dbg/consumers/Console/libconsole.a)
but if I do:
target_link_libraries(ConsoleDumperTest GTest::GTest GTest::Main PocoFoundation PocoUtil PocoNet ${YAML_LIBRARIES})
target_link_libraries(ConsoleDumperTest ../../../protocols/libprotocols.a)
target_link_libraries(ConsoleDumperTest ../../../consumers/libconsumers.a)
target_link_libraries(ConsoleDumperTest ../../../consumers/Console/libconsole.a)
I receive undefined references, despite of libraries contains in there directories. Which way I have to set path to link libraries?
According to documentation, target_link_libraries is not expected to work with relative paths: you should use either an absolute path, or a library name.
By knowing relative path, it is easy to construct absolute path in CMake. E.g., assuming you know relative path to the current source directory (the directory contained currently executed CMakeLists.txt), use CMAKE_CURRENT_SOURCE_DIR variable:
target_link_libraries(ConsoleDumperTest
${CMAKE_CURRENT_SOURCE_DIR}/../../../protocols/libprotocols.a
)
CMake's link_directories(...) command provides this:
link_directories(directory1 directory2 ...)
Specify the paths in which the linker should search for libraries. The command will apply only to targets created after it is called. Relative paths given to this command are interpreted as relative to the current source directory, see CMP0015.
Note also that, if these library dependencies are defined within the same CMake project, using add_library(target_name ...), you may specify the target name instead of the binary name within target_link_libraries.

C++ linux executable keeps trying to use library that does not exist

I am trying to write a simple application with GLFW on Linux. Right now the main file (the only file) is basically just a few lines of code to make sure the dynamic library linked correctly. Here it is:
#include <GLFW/glfw3.h>
#include <iostream>
int main()
{
glfwInit();
std::cout << "It works this far!" << std::endl;
glfwTerminate();
}
The include files are stored in a directory labelled "include" and the library files are stored in a directory labelled "lib". As of right now, I am compiling the program with the following line:
g++ -Wl,-Rlib -Iinclude -Llib test.cpp -o test -lglfw.3.2
It compiles and links just fine, but when I try to execute it, I get the following error:
./test: error while loading shared libraries: libglfw.so.3: cannot open shared object file: No such file or directory
Now, before you rush to downvote this question into oblivion and mark it as a duplicate, at least allow me to explain why I believe my question is different enough to not be a duplicate. I already attempted the solutions that the other questions presented, but it was unsuccessful. As you can see, I tried setting the path to the library during linking with the -Wl,-Rlib tag. I also tried setting LD_LIBRARY_PATH to point to the location of my libraries (the 'lib' folder), but it still threw the same error. (It didn't matter if the path was relative or absolute.)
So, the next thing I tried was running the ldd command on the executable. I got some other dependencies that were working just fine, but importantly, I got this:
libglfw.so.3 => not found
For some reason, it insists on looking for libglfw.so.3. It will not have it any other way. Upon renaming the library from libglfw.3.2.so to libglfw.so.3, the program executed just fine and printed It works this far! as if there were no problems at all.
Why would this happen?
For some reason, it insists on looking for libglfw.so.3. ... Upon renaming the library from libglfw.3.2.so to libglfw.so.3 ...
The ELF executables contain the exact name of the dynamic libraries used.
If the executable contains the library name "libglfw.so.3" the file must be named exactly like this.
The file naming scheme is intentionally done in a way that not the "full" version is coded into the file name: This way a later version ("libglfw.so.3.15") will work with the executable.
Normally there should be a symbolic link to the latest version of the library installed:
libglfw.so.3 -> libglfw.so.3.2
This symbolic link seems to be missing on your computer. I would say that this is an installation problem!
EDIT
The question could be: Why is the file name stored in the executable file not libglfw.3.2.so but libglfw.so.3?
The answer has to do with the backward compatibility when a new version of a library is installed:
Normally you would use the switch -lglfw and a symbolic link named libglfw.so is looked up.
If you stored the file name libglfw.so in the executable file and a new, incompatible version if this library (libglfw.so.4) is installed you would have no chance to get the program running by having both versions of the library installed.
To enable backward compatibility by having both versions of the library installed the "real" symbolic link name of the library (libglfw.so.3) must be stored in the executable file.
Therefore the "expected" file name of a library is stored in the library itself: Inside the file libglfw.so.3.2 you'll find some information that the file expects itself to be stored as libglfw.so.3.
The linker will use this information about the file name because it assumes that the library name given in the linker switch (-lglfw) is less "precise" than the name stored in the library itself.
For some reason, it insists on looking for libglfw.so.3. It will not have it any other way.
This is the Linux convention for shared libraries which is described here among other places. For Linux libfoo.so.x.y.z is considered to have the same ABI as libfoo.so.x. Usually when shared libraries are installed (e.g. via rpm, dpkg, etc.) there's an invocation of ldconfig that happens so that the just installed libraries have a symlink following the convention installed that references the library. Also these libs (if installed to a "trusted location"), are added to a linker cache for performance reasons.
It compiles and links just fine, but when I try to execute it, I get the following error:
./test: error while loading shared libraries: libglfw.so.3: cannot open shared object file: No such file or directory
libglfw.so.3 isn't on ld-linux.so's path.
As you can see, I tried setting the path to the library during linking with the -Wl,-Rlib
Still won't find it -- libglfw.so.3 isn't on ld-linux.so's path. You can add it by doing something like:
ldconfig -n /path/to/lib
Which should output the requisite libglfw.so.3 symlink for your lib.
IIRC setting the rpath might require a full path.
I also tried setting LD_LIBRARY_PATH to point to the location of my libraries
Again, libglfw.so.3 isn't on ld-linux.so's path.

C++ linux: dlopen can't find .so library

Reworded Question (although it's been solved already):
I've been having trouble using dlopen(3) to load a shared object library on linux. The library is part of a system of libraries built by me that are all loaded at runtime by a central executable. All of this is organized into a single workspace in Code::Blocks, where each project is given its own folder within a directory called Source, which is to be shipped with the program. The build directory of the executable is two directories backward from its own source code so that the exectuable and the Source folder are in the same directory, The libraries also build to the same directory as the executable, so naturally I pass the name of the library I'm trying to open as shown:
int main(int argc, char** argv) {
void* hLibrary = dlopen("libLibrary.so", RTLD_NOW | RTLD_GLOBAL);
if(hLibrary == NULL) {
fprintf(stderr, "%s\n", dlerror());
return 1;
}
return 0;
}
This was working at one point when the build directory was the same as the source code, until I changed the directories of the source code around to the arrangement described above. The problem at this point is that dlerror() returns "Cannot open libLibrary.so: no such file or directory," even though the file clearly exists and is in the same directory as the executable. I then tried passing in "/libLibrary.so" instead, because according to the man page on dlopen(3), adding a / indicates a relative directory. This returned the same error.
The solution to this was that a "./" was needed - where "." represents the working directory of the executable - and the working directory needed to be changed in Code::Blocks to where the executable was to be built. The following works perfectly:
void* hLibrary = dlopen("./libLibrary.so", RTLD_NOW | RTLD_GLOBAL);
This doesn't really show the full solution, but the following is basically the equivalent of what I'm doing:
void* hLibrary = dlopen("./../../libLibrary.so", RTLD_NOW | RTLD_GLOBAL);
Hopefully this explains the situation a little better.
Read the dlopen(3) man page (e.g. by typing man dlopen in a terminal on your machine):
If filename contains a slash ("/"), then it
is interpreted as a (relative or absolute) pathname. Otherwise, the
dynamic linker searches for the library as follows (see ld.so(8) for
further details):
o (ELF only) If the executable file for the calling program
contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag,
then the directories listed in the DT_RPATH tag are searched.
o If, at the time that the program was started, the environment
variable LD_LIBRARY_PATH was defined to contain a colon-separated
list of directories, then these are searched. (As a security
measure this variable is ignored for set-user-ID and set-group-ID
programs.)
o (ELF only) If the executable file for the calling program
contains a DT_RUNPATH tag, then the directories listed in that
tag are searched.
o The cache file /etc/ld.so.cache (maintained by ldconfig(8)) is
checked to see whether it contains an entry for filename.
o The directories /lib and /usr/lib are searched (in that order).
So you need to call dlopen("./libLibraryName.so", RTLD_NOW) -not just dlopen("libLibraryName.so", RTLD_NOW) which wants your plugin to be in your $LD_LIBRARY_PATH on in /usr/lib/ etc .... - or add . to your LD_LIBRARY_PATH (which I don't recommend for security reasons).
As Jhonnash answered you should use and display the result of dlerror when dlopen (or dlsym) fails:
void* dlh = dlopen("./libLibraryName.so", RTLD_NOW);
if (!dlh)
{ fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(EXIT_FAILURE); };
You might want to read some books like Advanced Linux Programming to get some knowledge about Linux system programming in general.
About the dlopen is defined; Dynamic library dlopen error can be checked. Undefined symbol error by this link.