Linux executable can't find shared library in same folder - c++

I am relatively new to Linux development, having been using Windows for a while now. Anyway, I am compiling a C++ game using g++ on both Windows and Linux (using mingw32 when needed), and am linking against SDL2 and SDL2_mixer. On Windows, one would only need to put the DLL files in the same folder as the executable and everything would run fine. On Linux however, although the code compiled just fine with not even a single warning, I get this at runtime :
./nKaruga: error while loading shared libraries: libSDL2_mixer-2.0.so.0: cannot open shared object file: No such file or directory
although said shared lib is in the same folder. I looked up several similar cases on Stack Overflow, all of them involving the use of LD_LIBRARY_PATH, and tried it but to no avail.
% LD_LIBRARY_PATH=pwd
% export LD_LIBRARY_PATH
% ./nKaruga
./nKaruga: error while loading shared libraries: libSDL2_mixer-2.0.so.0: cannot open shared object file: No such file or directory
I want to distribute this program on systems that do not necessarily have admin rights to install dependencies, hence why I am putting the SO in the same folder as the executable.
Thanks by advance !

LD_LIBRARY_PATH is a quick ad-hoc hack to specify alternate library loading search paths. A more permanent and cleaner solution is to specify the specific sets of paths in which libraries shall be searched specific for your particular binary. This is called the rpath (Wikipedia article on it: https://en.wikipedia.org/wiki/Rpath). There are a number of "variables" that can be specified in the binary rpath that get substituted. In your case the rpath variable ${ORIGIN} would be the most interesting for you. ${ORIGIN} tells the dynamic linker to look for libraries within the very same directory in which also the binary resides.
The rpath can be set at link time with the -rpath linker option, i.e. when invoked through GCC the option would be -Wl,-rpath='${ORIGIN}', i.e.
gcc -o program_binary -Wl,-rpath='${ORIGIN}' -lSDL2_mixer a.o b.o …
For an existing binary the rpath can be set post-hoc using the chrpath or patchelf tools; it's better to set it at link time proper, though.

Related

How to let Cmake Install to link my executables to my target shared libraries [duplicate]

I have a question related to how to install a built executable program with cmake when it relies on some external libraries. Suppose my executable is abc, and it relies on two external libraries: lib1.so and lib2.so. The structure of the codes are as follows:
-.........
|----bin (lib1.so lib2.so)
|----include(lib1.h lib2.h)
|----src(main.cpp)
When the executable program is installed using the following cmake commands:
INSTALL(TARGETS ${Exe_Name}
RUNTIME DESTINATION Path to bin
LIBRARY DESTINATION Path to bin)
I expect that the executable program will be in the same directory with lib1.so and lib2.so. However, when I execute the built program in the installation folder, I met the following error:
error while loading shared libraries: lib1 can not open shared object file No such file or directory
If I use ldd to check the executable, I found lib1.so and lib2.so not found. After searching for possible solutions, I found if I call the executable in this way, then it worked:
LD_LIBRARY_PATH=./ ./my_program_run
Then my question is how I can let my executable program knows the locations of the shared libraries with cmake when it is installed? Thanks.
This is best solved this with the RPATH of the final executable. RPATH is a hardcoded search path for the executable itself, and allows the use of the string $ORIGIN, which expands to the location of the executable at runtime. See this reference: http://man7.org/linux/man-pages/man8/ld.so.8.html
CMake strips the rpath of a binary at installation time, to avoid the binary picking up libraries littered around your development tree. But it also provides a simple way to modify the installation rpath for exactly this reason. Here's the short answer:
IF(UNIX)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:\$ORIGIN/../bin:\$ORIGIN")
ENDIF()
This particular example appends to the existing rpath, and adds . and ../bin to the search path, all relative to the location of the binary.
Some developers claim that adjusting the RPATH of the binary is not a good idea. In the ideal world, all the libraries would live in the system library directories. But if you take this to the extreme, you end up with Windows (at least the older ones), where c:\windows\system32 is full of junk that came from who knows where, and may or may not conflict with other software, etc. Using rpath and installing everything in one place seems like a great solution.
If the application is to be cleanly installed to a standard linux distribution, then you should either install the supporting shared libraries into a standard location (/usr/lib), or you should add the libraries location to the ld.so config, by create an /etc/ld.so.conf.d/myprogram.conf file containing the name of the directory the libraries are in.
If the installation is temporary or more ad-hoc, then a script to set the LD_LIBRARY_PATH is suitable.
The libraries are searched in the predefined locations which includes standard library paths configured with ld.so.conf and LD_LIBRARY_PATH. You can also try to compile your app with -rpath, but it is not recommended by some developers. I suggest you to create a wrapper script which will set LD_LIBRARY_PATH and run the real application like that:
"theapp" script:
#!/bin/sh
dir="`dirname \"$0\"`"
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}"$dir"
exec "$dir/theapp.real" # your real application
The application, script and libraries should be in the same location (under bin/).

Telling omake to use static version of a c library

I'm using omake to build a native binary executable. After it links and i try to run it, it fails to run giving the following error:
error while loading shared libraries: libprotobuf-c.so.1: cannot open shared object file: No such file or directory
Is there a way, at compile time, to tell the executable to pick up the static version: libprotobuf-c.a and not the shared on?
I'm not familiar with omake but I believe flag to ocamlc you are looking for is dllpath:
-dllpath dir
Adds the directory dir to the run-time search path for shared C libraries. At link-
time, shared libraries are searched in the standard search path (the one corresponding
to the -I option). The -dllpath option simply stores dir in the produced executable
file, where ocamlrun(1) can find it and use it.
If you can configure omake to pass the appropriate -dllpath argument to ocamlc, you should be good to go.
Under the hood I believe this is using the rpath feature (runtime library search path) of ld, the GNU linker. See https://linux.die.net/man/1/ld . There is also a chrpath utility to change the rpath of an already-built executable.
Another option is running your executable with LD_LIBRARY_PATH set so that the shared library is on the load path. You could also install the shared library system wide if appropriate. A final option is to load the library manually when your application boots using dlopen.
The correct choice depends on how you will distribute this and to who you will distribute this, if at all. Keep in mind if you use rpath/dllpath the end user is unlikely to have protobuf installed in the same location you do.
There doesn't seem to be a global flag that can be passed to ld, the linker, that enforces the linker prefer static libraries to dynamic ones when available. In my case, I set the library name explicitly like so:
OCAML_LINK_FLAGS += -cclib -l:libprotobuf-c.a

Code::Blocks and boost 1.55: static library is not used when dynamic library is present

I have built the boost 1.55 serialzation library with the following command:
b2 --build-dir=build toolset=gcc --with-serialization --layout=tagged link=static threading=multi stage
and got libboost_serialization-mt.a and libboost_wserialization-mt.a in my stage/lib directory - fine. Then I added boost_serialization to my C::B project's linker library list and compiled the boost serialization example and it ran fine from the command line. I then built the dynamic and single thread variants additionally using
b2 --build-dir=build toolset=gcc --with-serialization --layout=tagged link=static,shared threading=multi,single stage
and got more libraries in my stage/lib directory, as expected. What puzzles me is that there is a .so file for every library, even those that should be static. Why is it there? What is it needed for?
When I now compile the project, the executable complains:
error while loading shared libraries: libboost_serialization.so.1.55.0: cannot open shared object file: No such file or directory
The library is definitely there and I might just need to add the path to it to LD_LIBRARY_PATH, but I want to link statically for now. How can I do that?
I also don't quite understand the library naming: I have some libboost_wserialization... libraries in my lib folder and the w prefix to serialization is not described in the library naming section of the current boost getting started page.
Your answers gave me a better understanding of what was going on - now I know where the boost_wserialization libraries came from. I turned out that after doing the second build, all present libraries were shared, and the static libraries were overwritten. That's why I got confused by the "extra" .so files for those libraries which previously were indeed static.
Ok, first question:
Why is there a boost_serialization and boost_wserialization library?
the wserialization library is wchar_t oriented. Put into a separate library because it may not actually be needed.
Why are there multiple shared/static libraries?
The reason you're seeing all those extra shared libraries is because you're invoking b2 with link=static,shared, which instructs boost to build the shared libraries as well as the static libraries. Additionally, adding in the thread=multi causes the building of the mt libraries, which are libraries that should be used when linking to multi-threaded applications.
Why am I getting the run-time link error about libboost_serialization.so.1.55.0?
By default, most unix/linux systems will prefer the use of shared libraries over static libraries when linking, so when you try to link it will prefer to use the shared libraries over the static ones. If you want to force the link of the static library rather than the shared one, you tell the compile-time linker to do that, using:
-Wl,-Bstatic -lboost_serialization -Wl,-Bdynamic
This will cause the linker to look for the static variant of the boost_serialization library, rather than the dynamic one.
Now, because you're using code::blocks, I'd have to look up how to specify these flags on a case-by-case basis, but the most sensible thing to do is to clean the boost build using ./b2 clean and then rebuild, specifying only link=static, then you should end up with only .a files, which will produce stand-alone executables again.
If you want to specify this option for code::blocks, you would need to put them into the Build Settings -> Linker settings -> Other Linker Options field for the project. Simply specifying the library in the libraries field will not work for this case. Additionally, forgetting to pass in the -Wl,-Bdynamic option will cause it to try to link in static versions of some platform libraries, which can lead to build failure if the library in question is not present.
If you want to avoid having to set LD_LIBRARY_PATH to run the binary, you can add the option -Wl,-rpath,/path/to/boost/libraries, to the linker flags which will cause the compiled program to search that directory when trying to resolve the location of libraries.
What puzzles me is that there is a .so file for every library, even
those that should be static. Why is it there? What is it needed for?
You apparently are using someone else's make file. I wrote my own. My build command does not create a ".so" (shared object library). It only creates the ".a" (archive library). The linker knows how to use either.
See man ar. The utility ar builds archives.
See man ld. The utility ld can build shared objects.
You might look for these utility invocations in your build sequence, or ask someone where they are and comment out the use of ld, as you most likely do not need both (and building both will extend your build time unnecessarily). Alternatively, you might temporarily rename the ld command, and try your build. When it can not find the ld command, you might get a useful hint as to where the ld is invoked.
In my make file, the commands look like the following. The comment char is a # at beginning of line. (The string expansions $(AR) and $(LD) allow the use of non-standard utilities.)
$(TARGET_ARCHIVE): $(OBJ)
#echo R10: $(TARGET_ARCHIVE) :from: $(OBJ)
$(AR) crs $(TARGET_ARCHIVE) $(OBJ)
# $(TARGET_OLB) : $(OBJ)
# #echo R00: $(TARGET_OLB) :from: $(OBJ)
# $(LD) -o $(TARGET_OLB) -r $(OBJ)
The archive (.a), when used, is linked directly to and included in your executable. When the executable is loaded, all the referenced symbols of the .a are already in it. (un-referenced symbols and code are not linked in)
The shared object (.so) is not directly linked, but rather your executable gets a handle (or perhaps a file name) to the .so. It is my belief that when your executable is loaded, the .so is not immediately loaded. The .so does not load until the first time your executable references a symbol that is in the .so. At that loading, your app will experience a delay, but probably this late loading is reasonable for most applications.
It is also possible that the .so is already loaded in system memory before you activated your process. In that case, when your executable first references a symbol in the .so, some system code will 'map' the existing in-memory .so to your application -- probably faster than loading it, but I suppose the big benefit is that a .so that is used / referenced by many processes need only be loaded once, saving memory space. The loaded .so has all of its symbols, even if your app does not need all of them.
In either case, your executable will be smaller with .so's, bigger with .a's, but the .so's have some small performance hit for each .so that needs to be loaded or mapped in. With 4 GB in my desktop, the desktop has never felt 'crowded'. It's swap has never been used (afaik). So I generally use .a's.
NOTE: When the linker has access to both an archive (.a) and a shared object (.so) file, the linker will use the .so (and ignore the .a). Probably you can override that preference, but I have not tried. I find it easier to simply move the archive (.a) into a separate (from the .so's) directory, and inform the linker via the -L build option.

Statically linking to libarchive on Windows with MinGW

I've been using libarchive in my project for some time now and it's working great, at the moment I am dynamically linking to it, so on Windows the libarchive.dll file has to present on the system.
I would now like to statically link to the library so I don't have to bother distributing the DLL, but I'm having real trouble trying to achieve this!
Currently, in my make file, I have something like this:
-Lpath/to//libarchive/ -larchive
And this works, but it does a dynamic link. I don't know how to enforce a static link.
I can see in the libarchive directory there are two a files, libarchive.dll.a and libarchive_static.a. I suppose I want to link to libarchive_static.a but I can't seem to specify this, doing -larchive_static in the make file results in linker errors.
I was under the impression that static libraries under windows are lib files, but I get no such file type when I build libarchive.
How can I make a .lib file from libarchive. Also, as an extra question, what is the difference between an a file and a lib file?
Update
To statically link to libarchive, your library command for make should contain:
-Lpath/to//libarchive/ -larchive_static
This will link to the libarchive_static.a file. However, you also need to define LIBARCHIVE_STATIC in your code.
Now the problem is that libarchive depends on the bzip2 libraries (as well as others), and if you do not have a static build of them you will get linker errors something like:
undefined reference to `BZ2_bzCompressInit'
You need a static build of the dependent libraries and a similar command to the linker after the libarchive command:
-Lpath/to/bzip2/ -lbzip2
You can either build bzip2 from source, or do it the easy way and get a pre-built binary from the Gnu32Win project here: http://gnuwin32.sourceforge.net/packages.html
Just add libarchive_static.a explicitly to your link command.
gcc -o YourApp.exe $(OBJS) path/to/libarchive_static.a $(OtherLibs)
".lib" files differ from compiler to compiler (Borland, Microsoft etc.), ".a" is an old "archive" format from UNIX's ar tool. It is now used only for the bundling of static libraries.
Currently, in my make file, I have something ...
And this works, but it does a dynamic link
The .a file actually contains some code for dynamic linking to the .dll file, not the libarchive itself. On the startup the pointers to functions are allocated and dynamic linking is done.

C++ linking boost library

First I built the Boost libraries that require building by going to /usr/local/boost_1_49_0/ and running the bootstrap.sh. This went off alright.
Step (1) created all the .so and .a files in /usr/local/boost_1_49_0/stage/lib
I tested linking against a library, say lboost_regex and #include <boost/regex> in my source code. This also went off alright.
Finally trying out the example on asio, I tried:
g++ -I/usr/local/boost_1_49_0 MAIN.cpp -o MAIN -L/usr/local/boost_1_49_0/stage/lib -lboost_thread -lboost_system -lpthread
(4) compiled alright. But when I run the program with ./MAIN, I get the following error:
./MAIN: error while loading shared libraries: libboost_system.so.1.49.0: cannot open shared object file: No such file or directory
The -L option only sets a compile-time library search path; if you want a shared library to be found at runtime then its directory must be known at runtime.
One way to set this with g++ is to pass -rpath to the linker, via the compiler; in your case you could say -Wl,-rpath -Wl,/usr/local/boost_1_49_0/stage/lib. (This embeds the directory in the executable.)
Another way is to install the libraries in a place that the linker searches by default (e.g. /usr/local/lib might be such a place, depending on how the system is configured).
Yet another way is to set an environment variable such as LD_LIBRARY_PATH (Linux or Solaris) or DYLD_LIBRARY_PATH (Mac OS X), to tell the linker where to search when launching executables from the shell where the variable is set.
Are you sure the shared library is in a place where the loader can find it? Either put it in a system wide directory or the same directory as the executable.
Here's a link with more information about the loader.