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

Which is the correct way to build a static library with link time code generation on Linux?

我正在徘徊哪种是使用 GCC 在 Linux 上编译静态库的正确方法,这样当 link 时间优化 (LTO) 应用于可执行文件时,库将成为耗材,可能会达到最佳性能。

当仅使用 -flto 编译库时,无论是否使用 -flto 都无法对可执行文件进行 linked。错误是:

undefined reference to `hello'

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

根据对 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)

然后库可以 linked 为可执行文件,既可以使用 -flto 也可以不使用 -flto 传递给 linker 标志。

但是根据 this Stack Overflow 问题的答案,如果我们希望以这种方式编译静态库以便在有和没有 LTO 的情况下都可以使用,那么我们必须使用 -ffat-lto-objects。如果我们再次将此标志添加到库编译标志中,则可以将库 linked 为可执行文件,既可以使用 -flto 也可以不使用 -flto 传递给 linker 标志。

我的问题是:

  1. 第一个解 gcc-ar 的确切含义是什么?
  2. 当使用 -flto.

    编译库时,不同工作变体之间有什么区别

    2.1 无需 -flto.

    即可执行
    • 库仅使用 gcc-ar
    • 库仅使用 -ffat-lto-objects
    • 图书馆正在使用 gcc-ar-ffat-lto-objects

    2.2 可执行 -flto 和库的相同 3 个变体。

这是我的测试项目中的最小、完整且可验证的示例,它是 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");
}

说明:

  1. 使用选项 -flto -fno-fat-lto-objects 编译目标文件。 -fno-fat-lto-objects 不是绝对必要的,但是,它确保它要么执行 lto 要么失败(而不是回退到非 lto 模式)。
  2. 使用 ar rcsT --plugin <path-to-lto-plugin> ... 创建静态库您需要此处 liblto_plugin.so 的完整路径。此处的选项 T 创建一个 thin 存档(不复制 .o 文件)。
  3. Link 带有 -flto 标志。

示例:

$ 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

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

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

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

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

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