CMake and Static Linking
Asked Answered
G

3

36

I'm using CMake in a project, and I'm trying to statically link some libraries. I've set:

set(BUILD_SHARED_LIBS OFF)
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")
set_target_properties(icarus PROPERTIES LINK_SEARCH_END_STATIC 1)

And I've made sure when looking for the actual libraries that I have the *.a version of them.

Currently the project imports:

libPocoNet.a 
libPocoUtil.a 
libPocoXML.a 
libPocoFoundation.a 
libmysqlclient.a 
libmysqlpp.a 
libcrypto++.a 
CUDA

All libraries are found, and when doing a dynamic/shared linkage, they work fine. I have also tried to set compilation flags:

set(GCC_CXX_FLAGS ${GCC_CXX_FLAGS} "-static-libgcc -static-libstdc++ -static")

But to no avail. While I get no issues while compiling, linkage is throwing alot of undefined reference errors for calls found in the above libraries, i.e:

undefined reference to `mysql_thread_init'
undefined reference to `mysql_real_query'
undefined reference to `pthread_mutex_unlock'
undefined reference to `Poco::ErrorHandler::handle()'

Not in that particular order, and numerous errors for each library.

Looking at the last line of GCC I see:

/usr/bin/c++   -g -g  -static-libgcc -static-libstdc++ -static [list of *.cpp files]
-o icarus -rdynamic /usr/local/lib/libPocoFoundation.a /usr/local/lib/libPocoNet.a
/usr/local/lib/libPocoUtil.a /usr/local/lib/libPocoXML.a 
-Wl,-Bstatic -lmysqlclient -lmysqlpp -lcrypto++

Which makes me wonder:

  1. Why are Poco libraries linked as -rdynamic, and there is no -Wl -Bstatic flag? As if they are skipped/excluded from static linkage.
  2. mysqlclient, mysqlpp and crypto++ seem to be set for static linkage, yet I still get errors

So, could someone please explain to me:

  1. How do I setup for partial static linkage using CMake
  2. Is CMAKE_EXE_LINKER_FLAGS the only one I need to set?
  3. Should I be forcing static linking for mentioned libraries but not for entire project?

Please excuse me if those are too many or too localized questions, I haven't tried this before, and I can't seem to find much info on the net.

Gambit answered 7/6, 2013 at 19:4 Comment(0)
G
38

I've managed to solve my problem by using the following:

#Dynamic/Shared Libs
...
#Static start
set_target_properties(icarus PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(icarus PROPERTIES LINK_SEARCH_END_STATIC 1)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
#Static Libs
...
#Set Linker flags
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")

This works without passing a -static which creates other big issues, and can essentially mix static and dynamic libraries.

As long as the order of static libraries is correct, and as long as dependencies of static libraries are satisfied, I get an ELF using some dynamic libraries (i.e. in my case mysqlclient, libmysql++) and the rest are all static libraries (crypto++, PocoNet, PocoUtil, PocoXML, PocoFoundation).

Bear in mind that static linked libraries have their own dependencies. Examining my debug application using readelf -d app, I see:

Dynamic section at offset 0x508f88 contains 28 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libmysqlpp.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libmysqlclient.so.18]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]

I know that pthread is imported by Poco::Runnable, libm is for math operations, etc. I am still unaware if this is the right way to use CMake for partial static linking.

In the case of Debian packaged libraries, such as crypto++, mysql++, mysqlclient, simply finding the *.a library worked, but in case of Poco Libraries, which only got me the full path and name of the library, but not a flag, -Bdynamic could only be turned off by using the above lines.

Note: Poco could not be linked statically, without -static-libstdc++

I hope this helps anyone stuck at something similar.

Gambit answered 10/6, 2013 at 21:4 Comment(7)
I am getting a "/usr/bin/ld: cannot find -lc" (building on CentOS 7)Antimatter
@MikeM I find it hard to believe that the C library isn't installed. Maybe you need to export your library path?Nada
I ended up hardcoding the path to the .a file through add_library(..) and it worked for me (for both headers and static libraries)Antimatter
@MikeM if you care for portability this is not a good solution. Also I'm troubled as to why it won't find it, I'm guessing your library paths aren't exported, or CentOS uses unconventional lib paths?Nada
Well, it finds the .so file and the headers just fine. What was impossible is to default to the .a file for a static linkingAntimatter
is there portable solution for msvc and clang and gcc?Armstrong
@Armstrong I can't comment on msvc because I've never used it, but the above works with clang. This is cmake-specific not compiler-specific.Nada
B
23

How do I setup for static linkage using CMake

Well... you don't :) That's not how CMake works: in CMake, you first find the absolute path of a library, then link to it with target_link_libraries.

So, if you want to link to a static library, you need to search for that static library:

find_library(SOMELIB libsomelib.a)

instead of:

find_library(SOMELIB somelib)
Balustrade answered 7/6, 2013 at 21:27 Comment(6)
Hello again! That is what I've been doing, searching for the *.a and getting them. And that is how I still get the link errors. Should I be sending the absolute path of each static library to the linker instead?Nada
Yeah, CMake expects absolute paths and that's what find_library should give you.Balustrade
Well that is what I get. While running cmake, I'm logging the library names, and I can see the full path and *.a which is usually a symbolic link to the actual static lib. It still does not explain the no-reference errors though, does it? Those errors seem to be coming from within the libraries. Do I need to import each library's dependencies as well?Nada
it might be a problem with libraries order, try moving -lmysqlclient -lmysqlpp to the beginning in the linker and see if it improves.Balustrade
Well you were partially right, order did play a role. Turns out -lmysqlclient needs -lz to static link. Also -lcrypto++ cannot link with gcc.4.7.2. Thank you for pointing me to the right direction :-)Nada
You shouldn't need to find the absolute path(s) for libstdc* and then explicitly link, though, right??Tawannatawdry
L
0

For recently verions of cmake, it smart enough to link lib as static if you specify full file name of .a file. for example, the following line

TARGET_LINK_LIBRARIES(pfs ... liburing.a  ...)

it will generate compile cmd line:

ld ... -Wl,-Bstatic -luring -Wl,-Bdynamic ...

while cmd line the syntax is GCC's syntax to link against static lib. https://mcmap.net/q/270658/-how-can-i-force-linking-with-a-static-library-when-a-shared-library-of-same-name-is-present

Lenard answered 21/11, 2022 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.