在 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 标志。
我的问题是:
- 第一个解
gcc-ar
的确切含义是什么?
当使用 -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");
}
说明:
- 使用选项
-flto -fno-fat-lto-objects
编译目标文件。 -fno-fat-lto-objects
不是绝对必要的,但是,它确保它要么执行 lto 要么失败(而不是回退到非 lto 模式)。
- 使用
ar rcsT --plugin <path-to-lto-plugin> ...
创建静态库您需要此处 liblto_plugin.so
的完整路径。此处的选项 T
创建一个 thin 存档(不复制 .o
文件)。
- 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 信息被忽略)。
我正在徘徊哪种是使用 GCC 在 Linux 上编译静态库的正确方法,这样当 link 时间优化 (LTO) 应用于可执行文件时,库将成为耗材,可能会达到最佳性能。
当仅使用 -flto
编译库时,无论是否使用 -flto
都无法对可执行文件进行 linked。错误是:
undefined reference to `hello'
其中 hello
是库中定义的函数。
根据对
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 标志。
我的问题是:
- 第一个解
gcc-ar
的确切含义是什么? 当使用
编译库时,不同工作变体之间有什么区别-flto
.2.1 无需
即可执行-flto
.- 库仅使用
gcc-ar
。 - 库仅使用
-ffat-lto-objects
。 - 图书馆正在使用
gcc-ar
和-ffat-lto-objects
2.2 可执行
-flto
和库的相同 3 个变体。- 库仅使用
这是我的测试项目中的最小、完整且可验证的示例,它是 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");
}
说明:
- 使用选项
-flto -fno-fat-lto-objects
编译目标文件。-fno-fat-lto-objects
不是绝对必要的,但是,它确保它要么执行 lto 要么失败(而不是回退到非 lto 模式)。 - 使用
ar rcsT --plugin <path-to-lto-plugin> ...
创建静态库您需要此处liblto_plugin.so
的完整路径。此处的选项T
创建一个 thin 存档(不复制.o
文件)。 - 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 信息被忽略)。