在Linux上使用链接时间代码生成构建静态库的正确方法是什么?

时间:2017-10-25 13:44:45

标签: c++ c gcc linker lto

我正在游荡,这是使用GCC在Linux上编译静态库的正确方法,这样当链接时间优化(LTO)应用于可执行文件时,库可以被消耗并且可能实现最佳性能。 / p>

当只使用-flto编译库时,无论是否使用-flto,都无法将可执行文件链接到它。错误是:

  

对'hello'的未定义引用

其中hello是库中定义的函数。

根据this Stack Overflow问题的答案,一个可能的解决方案如下:

set(CMAKE_AR gcc-ar)
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH true)

然后可以将库链接到可执行文件,并将-flto-flto传递给链接器标记。

但是根据this Stack Overflow问题的答案,我们是否希望以这种方式编译静态库,以便在使用和不使用LTO时使用,而不是使用-ffat-lto-objects。如果我们再次将此标志添加到库编译标志中,则库可以链接到可执行文件,并且-flto-flto都不会传递给链接器标记。

我的问题是:

  1. 使用gcc-ar的第一个解决方案的确切含义是什么?
  2. 使用-flto编译库时,不同工作变体之间的区别是什么。

    2.1不带-flto的可执行文件。

    • 图书馆仅使用gcc-ar
    • 图书馆仅使用-ffat-lto-objects
    • 图书馆同时使用gcc-ar-ffat-lto-objects

    2.2使用-flto和库的相同3种变体可执行。

  3. 这是我的测试项目的Minimal,Complete和Verifiable示例,它是this Stack Overflow问题的示例的修改版本。我正在使用GCC版本7.2.0

    的CMakeLists.txt

    cmake_minimum_required(VERSION 3.9)
    
    project(lto)
    
    set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -g -O3")
    
    add_subdirectory(lib)
    add_subdirectory(exe)
    

    EXE /的CMakeLists.txt

    set(TARGET_NAME exe)
    
    add_executable(${TARGET_NAME} src/main.c)
    
    target_link_libraries(${TARGET_NAME} lib)
    
    option(EXE_LTO "Use link time optimizations for the executable." OFF)
    
    if(${EXE_LTO})
      target_compile_options(${TARGET_NAME} PRIVATE "-flto")
    endif()
    

    EXE / SRC / main.c中

    extern void hello();
    
    int main()
    {
      hello();
      return 0;
    }
    

    LIB /的CMakeLists.txt

    set(TARGET_NAME lib)
    
    add_library(${TARGET_NAME} STATIC src/lib.c)
    
    option(LIB_LTO "Use link time optimizations for the library." OFF)
    option(LIB_FAT_LTO "Create fat LTO objects for library files." OFF)
    option(LIB_USE_LTO_AR "Use another AR program for LTO objects." OFF)
    
    if(${LIB_LTO})
      target_compile_options(${TARGET_NAME} PRIVATE -flto)
    endif()
    
    if(${LIB_FAT_LTO})
      target_compile_options(${TARGET_NAME} PRIVATE -ffat-lto-objects)
    endif()
    
    if(${LIB_USE_LTO_AR})
      set(CMAKE_AR gcc-ar)
      set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
      set(CMAKE_C_ARCHIVE_FINISH true)
    endif()
    

    LIB / SRC / lib.c

    #include <stdio.h>
    
    void hello()
    {
      puts("Hello");
    }
    

2 个答案:

答案 0 :(得分:2)

说明:

  1. 使用选项-flto -fno-fat-lto-objects编译目标文件。 -fno-fat-lto-objects并非绝对必要,但是,它确保它可以执行或失败(而不是回退到非lto模式)。
  2. 使用ar rcsT --plugin <path-to-lto-plugin> ...创建静态库您需要liblto_plugin.so的完整路径。选项T此处会创建精简存档(不会复制.o个文件)。
  3. -flto旗帜相关联。
  4. 示例:

    $ cat library.cc
    namespace library {
    
    int f(int a) {
        return a + 1;
    }
    
    }
    $ cat test.cc
    #include <iostream>
    
    namespace library { int f(int); }
    
    int main() {
        std::cout << library::f(0) << '\n';
    }
    $ g++ -c -Wall -Wextra -std=gnu++14 -O3 -flto -fno-fat-lto-objects -o library.o library.cc
    $ ar rcsT --plugin /usr/libexec/gcc/x86_64-redhat-linux/5.3.1/liblto_plugin.so library.a library.o 
    $ g++ -c -Wall -Wextra -std=gnu++14 -O3 -flto -fno-fat-lto-objects -o test.o test.cc
    $ g++ -o test -flto -O3 test.o 
    /tmp/ccY0l2jQ.ltrans0.ltrans.o: In function `main':
    <artificial>:(.text.startup+0x37): undefined reference to `library::f(int)'
    collect2: error: ld returned 1 exit status
    $ g++ -o test -flto test.o library.a
    $ ./test
    1
    

答案 1 :(得分:2)

如果您未将--plugin /psth/to/lto-plugin.so*添加到ar参数,则会在链接时处获得未定义的引用,并在存档创建时获得警告。或者至少那是我得到的。你可能需要在这里添加它:

set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> --plugin <LTO_PLUGIN_PATH> \
  qcs <TARGET> <OBJECTS>")

LINK_FLAGS可能不属于这里,所以我省略了它们。)

我不知道如何自动设置LTO_PLUGIN_PATH。

该插件允许ar创建启用LTO的存档。所有其他方法要么根本不起作用(归档中的非胖对象),要么实际上不会导致链接时间优化(归档中的胖对象 - 在这种情况下只使用传统的目标代码,LTO信息被忽略了。)