链接 LLVM 导致 gcov 失败
Linking LLVM causes gcov to fail
将 --coverage
传递给 gcc 同时链接 LLVM 会导致链接器出现 undefined reference to `__gcov_exit'
错误。我已经建立了一个新项目来尝试隔离这个问题。你可以 view the source on github and inspect the compiler output on Travis-CI.
这是覆盖构建和非覆盖构建之间的区别
-DCMAKE_CXX_FLAGS="--coverage"
这是 LLVM 和非 LLVM 构建之间的区别
target_link_libraries(Test
PUBLIC
LLVMCore
)
LLVM
作业成功。 Coverage
作业成功。 LLVM + Coverage
作业失败并出现此错误
undefined reference to `__gcov_exit'
备注:
- 检查 [cmake 3.13]: CMAKE_<LANG>_FLAGS 以了解有关哪个环境变量影响哪个 cmake 变量的详细信息 - 正如您在(已删除的)答案
中发现的那样
- 只注意到cxx_std_17来自cmake 3.8,所以你可能想要更新您的 cmake_minimum_required
- 将你的 repo 分叉到 [GitHub]: CristiFati/gcov_error - An attempt to reproduce the gcov/llvm linker error 并对其进行了一些调试 - 注意,我可能有一天会删除它
- 能够理解 Travis CI,这是 真正的金矿
一开始我认为这将是一个微不足道的修复(与 -fprofile-arcs、-ftest-coverage 有关的东西, -lgcov 标志)如 [man7]: GCC(1) 中所述(--coverage 选项),但事实并非如此。
我无法在 Ubtu 16 x64 VM 上重现该问题(尽管 travis 非常好很好,出于调试目的,它有点慢(尤其是由于匆忙,编辑时可能会忘记或添加额外的字符 :)),而且它不提供与本地机器相同的访问级别,)因为环境:
- cmake 3.5.1
- gcc 5.4.0
- llvm 3.8.0
与 travis docker 图像上的内容相去甚远。我必须提到,我既没有尝试从源代码构建包(这可能会引起很多麻烦),也没有尝试从 CI[=179 期间下载它们的存储库下载它们=] 构建(甚至没有检查那些回购协议是否 public)。无论如何,VM 几乎无法使用,因为:
- 我无法重现问题(经过一些小改动以适应旧工具后,)一切正常
- 更多: 我运行变成了[SO]: Error when using CMake with LLVM - (@CristiFati's answer)
它正在吞噬我,所以我最终构建了 48 次(在 travis) 为了让它工作,那只是因为我没有注意到显而易见的事情。
问题
对于 3 个配置中的每一个,我将粘贴 compile 和 link(g++) 生成的命令(来自你的 build: [Travis CI]: Kerndog73 / gcov_error
- 构建 #24)
LLVM:
/usr/bin/g++-7 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/usr/lib/llvm-7/include -std=gnu++1z -o CMakeFiles/Test.dir/main.cpp.o -c /home/travis/build/Kerndog73/gcov_error/main.cpp
/usr/bin/g++-7 -rdynamic CMakeFiles/Test.dir/main.cpp.o -o Test -L/usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/llvm-7/lib/libLLVMCore.a /usr/lib/llvm-7/lib/libLLVMBinaryFormat.a /usr/lib/llvm-7/lib/libLLVMSupport.a -lz -lrt -ldl -ltinfo -lpthread -lm /usr/lib/llvm-7/lib/libLLVMDemangle.a
覆盖率:
/usr/bin/g++-7 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/usr/lib/llvm-7/include --coverage -std=gnu++1z -o CMakeFiles/Test.dir/main.cpp.o -c /home/travis/build/Kerndog73/gcov_error/main.cpp
/usr/bin/g++-7 --coverage -rdynamic CMakeFiles/Test.dir/main.cpp.o -o Test
LLVM + 覆盖率:
/usr/bin/g++-7 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/usr/lib/llvm-7/include --coverage -std=gnu++1z -o CMakeFiles/Test.dir/main.cpp.o -c /home/travis/build/Kerndog73/gcov_error/main.cpp
/usr/bin/g++-7 --coverage -rdynamic CMakeFiles/Test.dir/main.cpp.o -o Test -L/usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/llvm-7/lib/libLLVMCore.a /usr/lib/llvm-7/lib/libLLVMBinaryFormat.a /usr/lib/llvm-7/lib/libLLVMSupport.a -lz -lrt -ldl -ltinfo -lpthread -lm /usr/lib/llvm-7/lib/libLLVMDemangle.a
追了很多鬼,发现-L/usr/lib/gcc/x86_64-linux-gnu/4.8被传给了link呃,当包含 llvm 时。 gcc 4.8 is安装在docker上,所以显然gcc 7.4用于编译,gcc 4.8用于link ing,即 未定义行为。
我还粘贴了 collect 命令(更接近 linker) -v) of #3.(所有用-l指定的库* 且没有完整路径(例如 -lgcov、-lgcc_s、-lgcc) 有错误的版本,因为将从错误的目录中获取):
/usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccyDh97q.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o Test /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8 -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. CMakeFiles/Test.dir/main.cpp.o /usr/lib/llvm-7/lib/libLLVMCore.a /usr/lib/llvm-7/lib/libLLVMBinaryFormat.a /usr/lib/llvm-7/lib/libLLVMSupport.a -lz -lrt -ldl -ltinfo -lpthread /usr/lib/llvm-7/lib/libLLVMDemangle.a -lstdc++ -lm -lgcov -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/7/crtend.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.0
放置在 .travis.yml 的 after_script 部分的一些测试命令产生了以下输出:
$ _GCOV_LIB7=/usr/lib/gcc/x86_64-linux-gnu/7/libgcov.a
$ (nm -S ${_GCOV_LIB7} 2>/dev/null | grep __gcov_exit) || echo ${_GCOV_LIB7}
0000000000001e40 000000000000008b T __gcov_exit
$ _GCOV_LIB48=/usr/lib/gcc/x86_64-linux-gnu/4.8/libgcov.a
$ (nm -S ${_GCOV_LIB48} 2>/dev/null | grep __gcov_exit) || echo ${_GCOV_LIB48}
/usr/lib/gcc/x86_64-linux-gnu/4.8/libgcov.a
因此,libgcc (libgcov.a) 在涉及的两个版本 (__gcov_exit 符号已添加),g++ 知道它有一个但 ld 没有提供它(因为错误的库)因此出现错误。
现在,为什么 llvm 添加这个库搜索路径,我不知道(可能是因为它是用 gcc 4.8 - 或者接近它),但这是我能想到的:
- llvm 不适合与 gcc 7 一起使用(尽管我在快速浏览时没有发现任何此类限制 [LLVM]: Getting Started with the LLVM System)
- llvm 中的错误(考虑到我 运行 遇到的另一个问题,我倾向于认为这是赢家)
我找到了他们两个的方法:
使用较旧的 g++(4.8 - 默认安装在 docker)
export CXX=g++
(甚至可能没有必要)
- g++(不是cmake这次)不知道cxx_std_17,所以最简单的方法就是去掉传入的target_compile_features(缺点是代码不会C++17"compliant")
因为我不知道如何 "undo" 传递(错误的)库搜索路径,我想出的解决方法是指定正确的 在之前。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.2)
project(gcov_test)
find_package(LLVM 7.0.0 REQUIRED CONFIG)
message(STATUS "Found LLVM: ${LLVM_DIR} ${LLVM_PACKAGE_VERSION}")
add_executable(Test
"main.cpp"
)
target_compile_features(Test
PUBLIC cxx_std_17
)
target_include_directories(Test
PUBLIC ${LLVM_INCLUDE_DIRS}
)
target_compile_definitions(Test
PUBLIC ${LLVM_DEFINITIONS}
)
if(LINK_WITH_LLVM)
# @TODO - cfati: Everything in this block is to avoid hardcoding default libgcc path (/usr/lib/gcc/x86_64-linux-gnu/7)
set(_COLLECT_LTO_WRAPPER_TEXT "COLLECT_LTO_WRAPPER=")
execute_process(
COMMAND bash -c "[=15=] -v 2>&1 | grep ${_COLLECT_LTO_WRAPPER_TEXT} | sed s/${_COLLECT_LTO_WRAPPER_TEXT}//" "${CMAKE_CXX_COMPILER}"
OUTPUT_VARIABLE _GAINARIE_COLLECT_TMP_VAR
)
get_filename_component(_GAINARIE_GCC_DEFAULT_PATH ${_GAINARIE_COLLECT_TMP_VAR} DIRECTORY)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${_GAINARIE_GCC_DEFAULT_PATH}")
#set (GCCOPT "${GCCOPT} -L${_GAINARIE_GCC_DEFAULT_PATH}")
# @TODO END
target_link_libraries(Test
PUBLIC LLVMCore
)
endif()
注意:我试图想出更优雅的东西(我确信它是),但我做不到(尝试使用CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES、target_link_libraries、link_directories 等等)。
将 --coverage
传递给 gcc 同时链接 LLVM 会导致链接器出现 undefined reference to `__gcov_exit'
错误。我已经建立了一个新项目来尝试隔离这个问题。你可以 view the source on github and inspect the compiler output on Travis-CI.
这是覆盖构建和非覆盖构建之间的区别
-DCMAKE_CXX_FLAGS="--coverage"
这是 LLVM 和非 LLVM 构建之间的区别
target_link_libraries(Test
PUBLIC
LLVMCore
)
LLVM
作业成功。 Coverage
作业成功。 LLVM + Coverage
作业失败并出现此错误
undefined reference to `__gcov_exit'
备注:
- 检查 [cmake 3.13]: CMAKE_<LANG>_FLAGS 以了解有关哪个环境变量影响哪个 cmake 变量的详细信息 - 正如您在(已删除的)答案 中发现的那样
- 只注意到cxx_std_17来自cmake 3.8,所以你可能想要更新您的 cmake_minimum_required
- 将你的 repo 分叉到 [GitHub]: CristiFati/gcov_error - An attempt to reproduce the gcov/llvm linker error 并对其进行了一些调试 - 注意,我可能有一天会删除它
- 能够理解 Travis CI,这是 真正的金矿
一开始我认为这将是一个微不足道的修复(与 -fprofile-arcs、-ftest-coverage 有关的东西, -lgcov 标志)如 [man7]: GCC(1) 中所述(--coverage 选项),但事实并非如此。
我无法在 Ubtu 16 x64 VM 上重现该问题(尽管 travis 非常好很好,出于调试目的,它有点慢(尤其是由于匆忙,编辑时可能会忘记或添加额外的字符 :)),而且它不提供与本地机器相同的访问级别,)因为环境:
- cmake 3.5.1
- gcc 5.4.0
- llvm 3.8.0
与 travis docker 图像上的内容相去甚远。我必须提到,我既没有尝试从源代码构建包(这可能会引起很多麻烦),也没有尝试从 CI[=179 期间下载它们的存储库下载它们=] 构建(甚至没有检查那些回购协议是否 public)。无论如何,VM 几乎无法使用,因为:
- 我无法重现问题(经过一些小改动以适应旧工具后,)一切正常
- 更多: 我运行变成了[SO]: Error when using CMake with LLVM - (@CristiFati's answer)
它正在吞噬我,所以我最终构建了 48 次(在 travis) 为了让它工作,那只是因为我没有注意到显而易见的事情。
问题
对于 3 个配置中的每一个,我将粘贴 compile 和 link(g++) 生成的命令(来自你的 build: [Travis CI]: Kerndog73 / gcov_error - 构建 #24)
LLVM:
/usr/bin/g++-7 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/usr/lib/llvm-7/include -std=gnu++1z -o CMakeFiles/Test.dir/main.cpp.o -c /home/travis/build/Kerndog73/gcov_error/main.cpp /usr/bin/g++-7 -rdynamic CMakeFiles/Test.dir/main.cpp.o -o Test -L/usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/llvm-7/lib/libLLVMCore.a /usr/lib/llvm-7/lib/libLLVMBinaryFormat.a /usr/lib/llvm-7/lib/libLLVMSupport.a -lz -lrt -ldl -ltinfo -lpthread -lm /usr/lib/llvm-7/lib/libLLVMDemangle.a
覆盖率:
/usr/bin/g++-7 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/usr/lib/llvm-7/include --coverage -std=gnu++1z -o CMakeFiles/Test.dir/main.cpp.o -c /home/travis/build/Kerndog73/gcov_error/main.cpp /usr/bin/g++-7 --coverage -rdynamic CMakeFiles/Test.dir/main.cpp.o -o Test
LLVM + 覆盖率:
/usr/bin/g++-7 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/usr/lib/llvm-7/include --coverage -std=gnu++1z -o CMakeFiles/Test.dir/main.cpp.o -c /home/travis/build/Kerndog73/gcov_error/main.cpp /usr/bin/g++-7 --coverage -rdynamic CMakeFiles/Test.dir/main.cpp.o -o Test -L/usr/lib/gcc/x86_64-linux-gnu/4.8 /usr/lib/llvm-7/lib/libLLVMCore.a /usr/lib/llvm-7/lib/libLLVMBinaryFormat.a /usr/lib/llvm-7/lib/libLLVMSupport.a -lz -lrt -ldl -ltinfo -lpthread -lm /usr/lib/llvm-7/lib/libLLVMDemangle.a
追了很多鬼,发现-L/usr/lib/gcc/x86_64-linux-gnu/4.8被传给了link呃,当包含 llvm 时。 gcc 4.8 is安装在docker上,所以显然gcc 7.4用于编译,gcc 4.8用于link ing,即 未定义行为。
我还粘贴了 collect 命令(更接近 linker) -v) of #3.(所有用-l指定的库* 且没有完整路径(例如 -lgcov、-lgcc_s、-lgcc) 有错误的版本,因为将从错误的目录中获取):
/usr/lib/gcc/x86_64-linux-gnu/7/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/7/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper -plugin-opt=-fresolution=/tmp/ccyDh97q.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro -o Test /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/7/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.8 -L/usr/lib/gcc/x86_64-linux-gnu/7 -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/7/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/7/../../.. CMakeFiles/Test.dir/main.cpp.o /usr/lib/llvm-7/lib/libLLVMCore.a /usr/lib/llvm-7/lib/libLLVMBinaryFormat.a /usr/lib/llvm-7/lib/libLLVMSupport.a -lz -lrt -ldl -ltinfo -lpthread /usr/lib/llvm-7/lib/libLLVMDemangle.a -lstdc++ -lm -lgcov -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/7/crtend.o /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.0
放置在 .travis.yml 的 after_script 部分的一些测试命令产生了以下输出:
$ _GCOV_LIB7=/usr/lib/gcc/x86_64-linux-gnu/7/libgcov.a $ (nm -S ${_GCOV_LIB7} 2>/dev/null | grep __gcov_exit) || echo ${_GCOV_LIB7} 0000000000001e40 000000000000008b T __gcov_exit $ _GCOV_LIB48=/usr/lib/gcc/x86_64-linux-gnu/4.8/libgcov.a $ (nm -S ${_GCOV_LIB48} 2>/dev/null | grep __gcov_exit) || echo ${_GCOV_LIB48} /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcov.a
因此,libgcc (libgcov.a) 在涉及的两个版本 (__gcov_exit 符号已添加),g++ 知道它有一个但 ld 没有提供它(因为错误的库)因此出现错误。
现在,为什么 llvm 添加这个库搜索路径,我不知道(可能是因为它是用 gcc 4.8 - 或者接近它),但这是我能想到的:
- llvm 不适合与 gcc 7 一起使用(尽管我在快速浏览时没有发现任何此类限制 [LLVM]: Getting Started with the LLVM System)
- llvm 中的错误(考虑到我 运行 遇到的另一个问题,我倾向于认为这是赢家)
我找到了他们两个的方法:
使用较旧的 g++(4.8 - 默认安装在 docker)
export CXX=g++
(甚至可能没有必要)- g++(不是cmake这次)不知道cxx_std_17,所以最简单的方法就是去掉传入的target_compile_features(缺点是代码不会C++17"compliant")
因为我不知道如何 "undo" 传递(错误的)库搜索路径,我想出的解决方法是指定正确的 在之前。
CMakeLists.txt:
cmake_minimum_required(VERSION 3.2) project(gcov_test) find_package(LLVM 7.0.0 REQUIRED CONFIG) message(STATUS "Found LLVM: ${LLVM_DIR} ${LLVM_PACKAGE_VERSION}") add_executable(Test "main.cpp" ) target_compile_features(Test PUBLIC cxx_std_17 ) target_include_directories(Test PUBLIC ${LLVM_INCLUDE_DIRS} ) target_compile_definitions(Test PUBLIC ${LLVM_DEFINITIONS} ) if(LINK_WITH_LLVM) # @TODO - cfati: Everything in this block is to avoid hardcoding default libgcc path (/usr/lib/gcc/x86_64-linux-gnu/7) set(_COLLECT_LTO_WRAPPER_TEXT "COLLECT_LTO_WRAPPER=") execute_process( COMMAND bash -c "[=15=] -v 2>&1 | grep ${_COLLECT_LTO_WRAPPER_TEXT} | sed s/${_COLLECT_LTO_WRAPPER_TEXT}//" "${CMAKE_CXX_COMPILER}" OUTPUT_VARIABLE _GAINARIE_COLLECT_TMP_VAR ) get_filename_component(_GAINARIE_GCC_DEFAULT_PATH ${_GAINARIE_COLLECT_TMP_VAR} DIRECTORY) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${_GAINARIE_GCC_DEFAULT_PATH}") #set (GCCOPT "${GCCOPT} -L${_GAINARIE_GCC_DEFAULT_PATH}") # @TODO END target_link_libraries(Test PUBLIC LLVMCore ) endif()
注意:我试图想出更优雅的东西(我确信它是),但我做不到(尝试使用CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES、target_link_libraries、link_directories 等等)。