Please bear with this question, it is rather long.
TLDR: A CMake project with a subdirectory library links successfully, but creates a dynamic executable.
Code Available at: https://github.com/georcon/cmake-issue
Also Note: I have read all related questions/answers, and none answer this question.
I have created the following minimal example:
(Git Tag: SimpleExecutable)
main.c
#include <uuid/uuid.h>
#include <stdio.h>
int main(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
return 0;
}
CMakeLists.txt
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_executable(main-dynamic main.c)
target_link_libraries(main-dynamic uuid)
add_executable(main-static main.c)
target_link_libraries(main-static uuid -static)
Result
ldd main-dynamic
linux-vdso.so.1 (0x00007ffc406bb000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f76781cd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7677fdb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f76781eb000)
ldd main-static
not a dynamic executable
(Git Tag: ExecutableWithLibrary)
lib/lib.h
#ifndef LIB_H
#define LIB_H
void PrintUUID();
#endif //LIB_H
lib/lib.c
#include <uuid/uuid.h>
#include <stdio.h>
void PrintUUID(){
uuid_t some_uuid;
char uuid_str[40];
uuid_generate(some_uuid);
uuid_unparse(some_uuid, uuid_str);
printf("UUID: %s\n", uuid_str);
}
lib/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(testlibrary VERSION 1.0 DESCRIPTION "Static target issue - Library" LANGUAGES C)
add_library(testlib lib.c)
target_link_libraries(testlib uuid)
main.c
#include "lib/lib.h"
int main(){
PrintUUID();
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(cmake-issue VERSION 1.0 DESCRIPTION "Static target issue" LANGUAGES C)
add_subdirectory(lib ./bin)
link_directories(./lib/ ./bin)
add_executable(main-dynamic main.c)
add_dependencies(main-dynamic testlib)
target_link_libraries(main-dynamic libtestlib.a uuid -static)
link_libraries("-static")
add_executable(main-static main.c)
target_link_libraries(main-static PUBLIC "-static" libtestlib.a uuid)
add_dependencies(main-static testlib)
#target_link_libraries(main-static libtestlib.a uuid -static)
Result
ldd main-static
linux-vdso.so.1 (0x00007ffe6b485000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007fc67edd3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc67ebe1000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x00007fc67edec000)
Looking at the linker command:
~/cmake_issue$ cat CMakeFiles/main-static.dir/link.txt /usr/bin/cc
CMakeFiles/main-static.dir/main.c.o -o main-static
-L/home/georcon/cmake_issue/./lib -L/home/georcon/cmake_issue/./bin -Wl,-rpath,/home/georcon/cmake_issue/./lib:/home/georcon/cmake_issue/./bin -static -static -Wl,-Bstatic -ltestlib -Wl,-Bdynamic -luuid
Why does CMake not generate a statically-linked executable in this case?
Long story short: You need to tell CMake that you prefer static linking with the libraries.
This is done by setting property LINK_SEARCH_START_STATIC. Also you need to tell CMake to not reset static linkage at the end of the libraries list.
This is done by setting property LINK_SEARCH_END_STATIC:
set_target_properties(main-static PROPERTIES
LINK_SEARCH_START_STATIC ON
LINK_SEARCH_END_STATIC ON
)
See also that question: CMake and Static Linking.
Actually, the linker option -static not only disables PIE, but also affects on further libraries listed in the command... unless -dynamic is specified.
CMake has a notion about "default linking type", which is applied for every library (uuid in your case) for which CMake cannot deduce its type. Moreover, CMake maintains that default linking type after each library it adds into the linker's command line. And CMake expects the same behavior from the user, who manually adds linker flags.
Your first example is wrong, but suddenly works:
You add -static which turns current linkage type to static. And thus you break CMake expectations about current linkage type.
When produce the linker option for link with uuid, CMake expects that current linkage is dynamic. So, CMake doesn't add -dynamic linker switch.
That time CMake expectations doesn't correspond to the reality, which in turn corresponds to your expectations: uuid is linked statically.
But the second example reveals the problem:
When linking with libtestlib.a library, CMake is perfectly aware that this is a static library, and thus adds Wl,-Bstatic option before that library. But CMake need to maintain default linkage after every option, so it adds -Wl,-Bdynamic after the library:
-Wl,-Bstatic -ltestlib -Wl,-Bdynamic
With such options CMake expectations about default dynamic linking corresponds to the reality: uuid is linked dynamically. But now that reality doesn't correspond to yours expectations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With