Link libc statically - cmake

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 :-).

Related

Importing (RTEMS ) libraries in CMake

I am trying to import a library in CMake the modern way like shown in this thread:
How do I add a library path in cmake?
The goal is to build a RTEMS test program. I'm building on a Ubuntu 20.04 machine, and I am cross compiling for an ARM target with the arm/stm32h7 BSP.
The libraries are located inside an external lib folder.
I almost got the build process working, however CMake appears to do something which breaks the linking process. I propably did the mistake but I have problems figuring it out.
This is the basic setup of my CMake file, after I set up everything for cross compilation of RTEMS binaries:
...
# Here comes application stuff again
add_executable(${CMAKE_PROJECT_NAME} init.c led.c stm32h7xx_nucleo.c)
set(RTEMS_LIB_NAME "rtems_${RTEMS_ARCH_NAME}_${RTEMS_BSP_NAME}")
add_library(${RTEMS_LIB_NAME} SHARED IMPORTED)
set_target_properties(${RTEMS_LIB_NAME} PROPERTIES
IMPORTED_LOCATION ${RTEMS_BSP_LIB_PATH}
INTERFACE_INCLUDE_DIRECTORIES ${RTEMS_BSP_INC_PATH}
)
#target_link_directories(${RTEMS_LIB_NAME} INTERFACE
# ${RTEMS_BSP_LIB_PATH}
#)
#target_include_directories(${RTEMS_LIB_NAME} INTERFACE
# ${RTEMS_BSP_INC_PATH}
#)
target_link_options(${RTEMS_LIB_NAME} INTERFACE
# -I${RTEMS_BSP_INC_PATH}
# -B${RTEMS_BSP_LIB_PATH}
-Wl,--gc-sections
-Wl,-Bstatic
-Wl,-Bdynamic
-qrtems
)
target_link_libraries(${CMAKE_PROJECT_NAME} ${RTEMS_LIB_NAME})
Building the individual source files appears to work fine.
The raw link command attempted by CMake will be the following:
/home/rmueller/Documents/RTEMS/toolchain/rtems/6/bin/arm-rtems6-gcc
-mthumb -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard
-Wl,--gc-sections -Wl,-Bstatic -Wl,-Bdynamic
-qrtems CMakeFiles/blinky.dir/init.c.o CMakeFiles/blinky.dir/led.c.o CMakeFiles/blinky.dir/stm32h7xx_nucleo.c.o
-o blinky -Wl,-rpath,/home/rmueller/Documents/RTEMS/toolchain/rtems
/6/arm-rtems6/stm32h7 /home/rmueller/Documents/RTEMS/toolchain/rtems/6/arm-rtems6/stm32h7/lib
And I get the error:
./../../../arm-rtems6/bin/ld: cannot open linker script file linkcmds: No such file or directory
This is propably because the libraries are not in the search path somehow.
I then found out that the following command links the binary properly:
/home/rmueller/Documents/RTEMS/toolchain/rtems/6/bin/arm-rtems6-gcc
-mthumb -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard
-Wl,--gc-sections -Wl,-Bstatic -Wl,-Bdynamic
-qrtems CMakeFiles/blinky.dir/init.c.o CMakeFiles/blinky.dir/led.c.o
CMakeFiles/blinky.dir/stm32h7xx_nucleo.c.o -o blinky
-L/home/rmueller/Documents/RTEMS/toolchain/rtems/6/arm-rtems6/stm32h7/lib
Is the way to import the library wrong? I could just add the -L flag manually to my build target using commands like target_link_options , but I was thinking it would be nice if the search path could just be an interface requirement when linking the RTEMS library.
UPDATE: I think I found one error: I imported the library path as a SHARED library and it propably has to be imported as STATIC.
THe command now looks like this:
/home/rmueller/Documents/RTEMS/toolchain/rtems/6/bin/arm-rtems6-gcc -mthumb
-mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard -Wl,--gc-sections -Wl,-Bstatic -Wl,-Bdynamic -qrtems
CMakeFiles/blinky.dir/init.c.o CMakeFiles/blinky.dir/led.c.o CMakeFiles/blinky.dir/stm32h7xx_nucleo.c.o
-o blinky /home/rmueller/Documents/RTEMS/toolchain/rtems/6/arm-rtems6/stm32h7/lib
UPDATE2:
I solved the problem. There was still a little syntax error, I think the quotes were missing. The command to set the library properties looks like this now :
set_target_properties(${RTEMS_LIB_NAME} PROPERTIES
IMPORTED_LOCATION "${RTEMS_BSP_LIB_PATH}"
INTERFACE_INCLUDE_DIRECTORIES "${RTEMS_BSP_INC_PATH}"
)
And the binary is linked properly :-)
UPDATE3:
And it has stopped working again. This is really weird. The -L flag appears to be missing..
Kind Regards
Robin
Okay, I finally solved the issue. The above option is used to explicitely include libraries. In the RTEMS case, simply adding the library path and using -qrtems is sufficient.
The resulting and working CMakeLists.txt file can be found here: https://github.com/rmspacefish/rtems-demo/blob/master/applications/stm32/blinky/CMakeLists.txt

undefined reference to `shm_open' - how can I pinpoint the culprit?

I am trying to build a simple program against a library, which itself depends on librt. The library compiles just fine and the -lrt flag is used there. The program also builds fine on amd64 using cmake - but on arm64, it fails. This is not cross-compilation, but directly building it on the target. I'm using a normal cmake build system (cmake ..; make).
The exact same build system can also compile a different program, which uses the same library, but not the same functions from it.
Here is the build error:
[100%] Linking C executable mrun-talker
/usr/lib/gcc/aarch64-linux-gnu/7/../../../../lib/libsec-common.so: undefined reference to `shm_open'
/usr/lib/gcc/aarch64-linux-gnu/7/../../../../lib/libsec-common.so: undefined reference to `shm_unlink'
And here is the linker command:
/usr/bin/cc CMakeFiles/sec-talker.dir/main.c.o -o sec-talker -lsec-common -lsec-rosc -lsec-api -ltert -lgcov -lm -lrt -lpthread
The linker command does contain the -lrt flag at the end of the command and the lrt.so is available on the target.
Is there a chance that although the library compiles just fine, it does not properly link and causes this error later when I try to use it?
Full cmake-file:
get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME)
string(REPLACE " " "_" ProjectId ${ProjectId})
project(${ProjectId})
set(ExecName ${ProjectId})
enable_language(C)
find_package(Threads)
add_executable(${ExecName} main.c)
target_link_libraries(
${ExecName}
# sec libraries
sec-common
sec-rosc
sec-api
tert
# system libraries
gcov
m
Threads::Threads
rt
)
link_directories("/usr/local/lib")
install(TARGETS ${ExecName})
/edit
I used ldd to check the linking of the libsec-common. Here is the result of the (working) amd64 version:
# ldd /usr/lib/libsec-common.so
linux-vdso.so.1 (0x00007fff7e922000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffa350ef000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffa356ec000)
And of the (not-working) arm64 version.
# ldd /usr/lib/libsec-common.so
linux-vdso.so.1 (0x0000ffffb2dc6000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb2c26000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb2d9b000)
I don't see an issue here tbh.
The issue here was that the dependency itself did not correctly link it's dependency.

Force absolute path for shared library without LD_RUN_PATH

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.

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.

linking with particular version of a library

I am linking boost libraries with my .cpp files. The build machine has boost 1.55 in /usr/lib64 and I have boost 1.57 in my local directory. The cmake generates the following link command.
/home/ramki/mpich-install/bin/mpicxx -fopenmp -fexceptions -fno-use-linker-plugin CMakeFiles/factor.dir/factor.cpp.o CMakeFiles/factor.dir/factor_jobs.cpp.o -o factor -rdynamic -lboost_serialization -lboost_iostreams -lboost_program_options -lboost_mpi -llapack -llapacke -lblas -lpthread -lm -lz factorization/libfactorization.a
The above link command does not specify the version of the boost libraries. Because of this I get the following error.
/usr/bin/ld: warning: libboost_serialization.so.1.57.0, needed by /home/ramki/libraries/boost_1_57_0//lib/libboost_mpi.so, may conflict with libboost_serialization.so.1.55.0
Because of this nature of linking, when I use ldd to dump the linked libraries of the executable, I see it linked with couple of libboost 1.55 libraries. If the machine in which I run this executable does not have boost 1.55, it does not start at all.
In the CMakeLists.txt and CMakeCache.txt, I see that the find_package is discovering the 1.57 libraries.
find_package(Boost 1.57.0 COMPONENTS serialization iostreams program_options mpi REQUIRED).
However during linking it is not introducing the version of the library. How do I instruct the cmake to do the following.
linking libraries to use a particular version. For eg., -l:libboost_mpi.so.1.57.0
specify the library path for this version -L library path explicitly. It should NOT link with the library under /usr/lib64.
Ramki
Not sure if this will work for other libraries. Atleast for boost before find_package on boost, set (Boost_REALPATH ON). This will set Boost_LIBRARIES with full path as
/export5/home/ramki/libraries/boost_1_57_0/lib/libboost_serialization.so.1.57.0;/export5/home/ramki/libraries/boost_1_57_0/lib/libboost_iostreams.so.1.57.0;/export5/home/ramki/libraries/boost_1_57_0/lib/libboost_program_options.so.1.57.0;/export5/home/ramki/libraries/boost_1_57_0/lib/libboost_mpi.so.1.57.0.
Use this Boost_LIBRARIES with target_link_libraries(theTarget ${Boost_LIBRARIES}). Thus instead of linking with libboost_mpi.so that could be link to other versions, we are linking with the library of the correct version.