3

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

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

对“你好”的未定义引用

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)

然后可以将库链接到可执行文件,无论-flto是否-flto传递给链接器标志。

但是根据这个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 个变体相同的可执行文件。

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

2 回答 2

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 信息被忽略)。

于 2017-10-25T15:05:01.243 回答
2

指示:

  1. 使用选项编译您的目标文件-flto -fno-fat-lto-objects-fno-fat-lto-objects不是绝对必要的,但是,它确保它要么执行 lto 要么失败(而不是回退到非 lto 模式)。
  2. ar rcsT --plugin <path-to-lto-plugin> ...使用You need the full path to liblto_plugin.sohere创建静态库。此处的选项T创建一个精简存档(不复制.o文件)。
  3. -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
于 2017-10-25T14:59:08.987 回答