GCon
GCon

Reputation: 1527

Generate Static Executable with CMake

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:

Create a statically-linked executable (Works correctly)

(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

Create a static executable with library

(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?

Upvotes: 3

Views: 3818

Answers (1)

Tsyvarev
Tsyvarev

Reputation: 65928

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.

What is going on

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.

Upvotes: 2

Related Questions