Force absolute path for shared library without LD_RUN_PATH - g++

I am trying to link a locally installed shared library (./vendor/lib/libfoo.so) with my binary, ./bar. Unfortunately, none of my attempts generates a link with an absolute path to libfoo.so. As a consequence I need to use
LD_LIBRARY_PATH=vendor/lib ./bar
to run it, which I want to avoid. ldd bar shows me this:
linux-vdso.so.1 => (0x00007ffed5fd8000)
libbar.so.2 => not found
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fb9ea787000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb9ea47d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb9ea267000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb9e9e9d000)
/lib64/ld-linux-x86-64.so.2 (0x000055f326761000)
A word about libbar.so.2: the file exists (in vendor/lib) alongside libbar.so. Both are actually symlinks to libhts.so.1.6. That file also exists, and is the actual shared library.
Here’s the different ways I’ve tried:
FULL_PATH="$(pwd -P)/vendor/lib"
g++ -o bar bar.o -Lvendor/lib -lfoo # 1
g++ -o bar bar.o -L$FULL_PATH -lfoo # 2
g++ -o bar bar.o $FULL_PATH/libfoo.so # 3
g++ -o bar bar.o $FULL_PATH/libfoo.so.1.6 # 4
All of these variants produce identical ldd output, even the last line (does ld insist on using the highest version of a library?).
The only way I’ve found to make this work is to use
LD_RUN_PATH=$FULL_PATH g++ -o bar bar.o -Lvendor/lib -lfoo
(I can’t use -rpath because my version of g++ doesn’t understand this argument, and I’m using g++ instead of ld to get the libstdc++ dependencies right — I could use -Wl,-rpath of course.)
But I can’t help but feel that there should be a way of making this work without the use of environment variables/-rpath. I’ve found an answer specifically referencing symlinks to libraries but unfortunately it doesn’t help me (see attempt 4 above).
This is on Ubuntu 16.04, g++ 5.4.0, GNU ld 2.26.1, in case it matters.

It sounds likely that you didn't update the ldconfig cache after installing
your shared library in the non-standard location /what/ever/vendor/lib:-
sudo ldconfig /what/ever/vendor/lib
Until you do that the runtime linker will be unaware that libfoo.so is
in /what/ever/vendor/lib, even if it is, unless you prompt it at runtime through
the LD_LIBRARY_PATH environment variable.
Incidentally, it isn't a shortcoming of your version of g++ that it
doesn't recognize -rpath. This has only ever been a linker (ld) option,
never a GCC frontend option. So -Wl,-rpath=/what/ever/vendor/lib is the
conventional way of tacking the non-standard runtime library path to your
program so as to avoid relying on either the ldconfig cache or LD_LIBRARY_PATH
For out-of-the ordinary linkages it may be considered better to use -rpath
rather than extend the ldconfig cache, which has less discriminate effects.

Related

How can I properly configure the g++ include path with mingw64?

I have installed msys2/mingw64 because I need the g++ compiler. Now, I want to compile some c++ oce which requires openblas. I have installed the package using pacman -S mingw-w64-x86_64-openblas. However, compiling the code fails with
fatal error: cblas.h: No such file or directory
Clearly, the include path does not contain the headers from openblas which are located at C:\msys64\mings64\include\openblas. This is easy to fix by passing -I<include path> as an additional argument to g++.
Now, I was wondering whether there is an automated way to include include files/headers of installed packages in the g++ include path. The same problem also holds for libraries.
For example, pacman might be able to atomatically append these paths onto some environment variable which g++ checks.
The standard way to get compilation and linking options for a library on MSYS2 and other Unix-like systems is to run this command:
pkg-config --cflags --libs openblas
If you're just compiling, use --cflags by itself.
If you're just linking, use --libs by itself.
Here's an example Bash command you could use to compile a single-file program:
g++ foo.cpp $(pkg-config --cflags --libs openblas) -o foo

Link libc statically

I am trying to make a static executable with CMake 3.15. I am building on Alpine Linux (hence with musl), and currently, my executable's ldd output is:
# ldd my_executable
/lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x7fc6f65b3000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x7fc6f659f000)
I can set target_link_options(my_executable PRIVATE -static-libgcc -static-libstdc++), and they got linked statically:
# ldd my_executable
/lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fc6f7977000)
But I can't manage to get musl to link statically. I tried (with clean builds, to make sure that the configure step starts from zero):
Adding -static to target_link_options above.
set(CMAKE_EXE_LINKER_FLAGS "-static") before the call to add_executable(my_executable ...)
Adding -static to target_link_libraries(my_executable lib1 lib2 -static)
When I run CMake in VERBOSE=1 mode, it always ends the linking command with:
... -Wl,-Bdynamic -ldl -lrt -lm -lpthread
I believe that this is my issue: I want to get rid of that -Bdynamic. What am I missing? Is this -Bdynamic coming from one of my dependencies? I build them all from sources as static (.a) libraries, so how could they be linking libc dynamically? Or would I need to patch them all to add -static when I build them?
As hinted by KamilCuk's comment, the answer here seems to have the solution. Still, I'm not doing exactly the same, therefore I'll keep this answer, too.
For the target executable that I want statically linked:
add_executable(my_executable main.cpp)
I had to set the following properties/options:
set_target_properties(my_executable PROPERTIES LINK_SEARCH_START_STATIC ON)
set_target_properties(my_executable PROPERTIES LINK_SEARCH_END_STATIC ON)
target_link_options(my_executable PRIVATE -static-libgcc -static-libstdc++ -static)
Some notes:
LINK_SEARCH_*_STATIC were useful to remove -Bdynamic from the linking command.
I never managed to remove -ldl from the linking command, but it seems like dl did not get link eventually (presumably because it is not used).
ldd was not enough to verify that my_executable is statically linked. readelf -l my_executable showed that it does not have an INTERP header, and there is currently no such thing as a dynamic binary without it (using musl).
It turns out that checking whether a binary is statically linked or not is not so straightforward :-).

Error when linking GSL with -static

I have written a programm in c++. Linking and runiing is working, as long as I don't use the "-static" option for g++. But I have to run it from an Antergos USB-Live Stick with default settings and there is no GSL included. In the manual of GSL they recommend
$ g++ -c main.cpp
$ g++ -static main.o -lgsl -lgslcblas -lm -lnlopt
But for this code I receive an error message:
/usr/bin/ld: cannot find -lgsl
/usr/bin/ld: cannot find -lgslcblas
collect2: Fehler: ld gab 1 als End-Status zurück
I tried it as this question, but it didn't work for me. When I run
$ g++ -O2 -o test main.cpp -lgsl -lgslcblas -lnlopt -lm
$ lld test
it prints
linux-vdso.so.1 (0x00007fffa5b95000)
libgsl.so.19 => /usr/lib/libgsl.so.19 (0x00007f8748c9a000)
libgslcblas.so.0 => /usr/lib/libgslcblas.so.0 (0x00007f8748a5d000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f87486d5000)
libm.so.6 => /usr/lib/libm.so.6 (0x00007f87483d1000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f87481ba000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f8747e1c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f87490fe000)
So I tried to create a symlink, but I do have also "libgsl.so"
$ ls /usr/lib/libgsl
libgslcblas.so libgslcblas.so.0.0.0 libgsl.so.19
libgslcblas.so.0 libgsl.so libgsl.so.19.3.0
Am I doing something stupid? Thank your for your help.
When you pass -lgsl, by default you request the linker to
find and link either the shared library libgsl.so or the static
library libgsl.a and to prefer the shared library, if both are found
in the same search directory. The linker will search, first, in any
directories you have specified with the -L/path/to/search options,
in the order you specified, and then in its default search directories
(/usr/lib, etc.). Likewise for -lgslcblas.
But when you pass the linkage option -static to gcc/g++, it prevents
linking with any shared libraries. Shared libraries, libgsl.so, libgslcblas.so
will be ignored. Static libraries libgsl.a, libgslblas.a, must be
found, in some or other of the search directories, for the linkage to
succeed.
The linker is saying:
/usr/bin/ld: cannot find -lgsl
/usr/bin/ld: cannot find -lgslcblas
because it can't find those static libraries - presumably because you
haven't installed them.
You do not say what linux distro you are working on, but if the package
that provides libgsl and libgslcblas is called, say, libgsl[suffix]
then there will be a corresponding package called libgsl-dev, libgsl-devel,
or similar. This will be the development version of the package,
for the use of people who want to develop software that links with libgsl
or libgslcblas. The development package will require the libgsl package as a dependency
- so it will install the same stuff - and will in addition contain the
library's header files and the static version of the library.
So you need to install the libgsl development package for your distro.
For Ubuntu, for example, that is libgsl-dev:
Later
I gather that your distro, Arch Linux, does not do separate dev packages. You
need to build the static libraries from source. To do that you will need
at least to have installed:
GNU Make
GNU autotools (autoconf, automake, libtool)
GCC (C compiler)
texinfo
Then to make a default build:
Get the gsl source package from https://savannah.gnu.org/git/?group=gsl
either by cloning the git repo or downloading a current tar.gz tarball
and extracting it.
cd into the package directory.
run ./autogen.sh. This will succeed provided the GNU autotools prerequisites
are fulfilled.
run ./configure --enable-maintainer-mode (as ./autogen.sh will have prompted you).
This will succeed provided that the package dependencies are satisfied
and environment sanity checks pass.
run make
If make completes without errors - which will take a matter of minutes -
then, as root, run make install.
If all is well, this will install your missing static libraries:
/usr/local/lib/libgsl.a
/usr/local/lib/libgslcblas.a
You should not need to modify your linkage command for the linker to find
them: /usr/local/lib is a default linker search path.

Set multiple paths to CMAKE_Fortran_MODULE_DIRECTORY

So I am trying to include an installed Fortran library to a CMake project. I know the directory where the module (.mod) file is located, but my project can't seem to find it unless I set CMAKE_Fortran_MODULE_DIRECTORY.
SET(LIB_INCLUDE_DIR /usr/local/include)
SET(EX_FILES main.f90 file1.f90 file2.f90)
INCLUDE_DIRECTORIES(${LIB_INCLUDE_DIR})
ADD_EXECUTABLE(test ${EX_FILES})
TARGET_LINK_LIBRARIES(test lib1)
Where the error is
use lib1
1
Fatal Error: Can't open module file 'lib1.mod' for reading at (1): No such file or directory
unless I include the line
SET(CMAKE_Fortran_MODULE_DIRECTORY ${LIB_INCLUDE_DIR})
And then it can find the file just fine. But I'm running into a little problem. By setting CMAKE_Fortran_MODULE_DIRECTORY, CMake tries to write all generated modules to this directory rather than the CMAKE_BINARY_DIR (where I would like it).
From the documentation, I know that CMAKE_Fortan_MODULE_DIRECTORY is meant to be set to specify where to write generated module files, but some compilers look to that directory to find modules. Is there any way to set multiple directories so that if it can't find in/write to one directory, it searches the second? When I try to set CMAKE_Fortran_MODULE_DIRECTORY to be multiple directories, it only looks at the first directory.
If it helps I am on a Ubuntu 14.04 LTS system using gfortran 4.8.4
EDIT:
So by Alexander Vogt's suggestion I ran with VERBOSE=1 and got
cd /home/user/Repos/build/src && gfortran -I/home/user/Repos/build/src -isystem /usr/local/include -c /home/user/Repos/test_project/src/main.f90 -o CMakeFiles/test.dir/main.f90.o
when I did NOT set CMAKE_Fortran_MODULE_DIRECTORY and
cd /home/user/Repos/build/src && gfortran -J/usr/local/include -I/home/user/Repos/build/src -isystem /usr/local/include -c /home/user/Repos/test_project/src/main.f90 -o CMakeFiles/test.dir/main.f90.o
when I did.
It seems the only difference is the -J flag, which sets exactly what CMAKE_Fortran_MODULE_DIRECTORY sets. Is there some flag that sets just where to look for compiled modules, and not where to put them?

Compile C++ code to run on ESXi 3.5

I'm trying to compile a simple c++ program to run inside ESXi 3.5 console window. It seems I'm linking with wrong libraries... Is there a setup described somewhere - which version of G++ and libraries do I have to be using in order to do so?
Here's how I resolved the issue. I did following to compile:
Compiled using gcc under ubuntu
Ran ldd on executable
Copied all libraries that showed up as dependencies to subfolder ESXi-3.5-lib. In my case they were:
ld-linux.so.2
libc.so.6
libgcc_s.so.1
libm.so.6
libstdc++.so.5
Added following switches to gcc:
-nodefaultlibs (to not attempt to link with default libs)
-lc (prevented link error in some crt library)
-fno-stack-protector (prevented another error, some other function was missing)
Following was my final build command:
g++ file1.cpp file2.cpp file3.cpp -o output-biinary-file-name \
ESXi-3.5-lib/ld-linux.so.2 ESXi-3.5-lib/libc.so.6 ESXi-3.5-lib/libgcc_s.so.1\
ESXi-3.5-lib/libm.so.6 ESXi-3.5-lib/libstdc++.so.5 \
-nodefaultlibs -lc -m32 -fno-stack-protector