Связывание LLVM приводит к сбою gcov - PullRequest
0 голосов
/ 05 декабря 2018

Передача --coverage в gcc при подключении LLVM также вызывает ошибку undefined reference to `__gcov_exit' от компоновщика.Я создал новый проект, чтобы попытаться изолировать эту проблему.Вы можете просмотреть исходный код на github и проверить вывод компилятора на Travis-CI .

В этом разница между покрытием и сборкой без покрытия

-DCMAKE_CXX_FLAGS="--coverage"

В этом разница между сборкой LLVM и сборкой без LLVM

target_link_libraries(Test
        PUBLIC
        LLVMCore
)

Задание LLVM выполнено успешно.Работа Coverage выполнена успешно.LLVM + Coverage Задание не выполняется с этой ошибкой

undefined reference to `__gcov_exit'

Любая помощь по этому вопросу будет принята с благодарностью.

1 Ответ

0 голосов
/ 04 января 2019

Примечания :

Вначале я думал, что это будет тривиальное исправление (что-то связанное с -fprofile-arcs , -ftest-покрытие , -lgcov flags), как отмечено в [man7]: GCC (1) ( - опция покрытия ), но это не так.
Я не смогвоспроизвести проблему на моем Ubtu 16 x64 VM (хотя travis очень хорош, он довольно медленный для целей отладки (особенно когда из-за срочности можно забыть или добавить дополнительный символ при редактировании):)) а также он не предлагает тот же уровень доступа, что и локальный компьютер,) потому что среда:

  • cmake 3.5.1
  • gcc 5.4.0
  • llvm 3.8.0

довольно далеко от того, что находится на travis докер изображение.Должен отметить, что я не пытался собирать пакеты из исходников (что, вероятно, вызвало бы много головной боли), а также не пытался загружать их из репозиториев, куда они загружались во время сборок CI (даже непроверьте, являются ли эти репозитории общедоступными).В любом случае, VM было в значительной степени непригодным для использования, потому что:

Это пожирало меня живьем, поэтомуЯ закончил строить 48 офигенное время (на travis ), чтобы заставить его работать, и это только потому, что я не заметил очевидного.

Проблема

Для каждой из 3-х конфигураций я собираюсь вставить compile и link ( g ++ ) сгенерированные команды (из вашего build: [Travis CI]: Kerndog73 / gcov_error - Build # 24 )

  1. 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
    
  2. Покрытие :

    /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
    
  3. 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 передается компоновщику при включении llvm . gcc 4.8 установлен в докер , поэтому, очевидно, gcc 7.4 использовался для компиляции, а gcc 4.8 для связывания, что является неопределенным поведением .
Я также вставляю команду collect (ближе к компоновщику ) из небольшого изменения (с -v ) # 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 ) был изменен где-то между 2-мя задействованными версиями (был добавлен символ __ gcov_exit ),и g ++ знал, что он есть, но ld не предоставил его (из-за неправильной библиотеки), отсюда и ошибка.

Теперь, почему добавляет llvm добавить этот путь поиска в библиотеке, я не знаю (возможно, потому что он был построен с gcc 4.8 - или чем-то близким к нему), но вот что я могу придумать:

  1. llvm не предназначен для использования с gcc 7 (хотя я не нашел такого ограничения при быстром просмотре [LLVM]: Начало работы с системой LLVM )
  2. Ошибка в llvm (учитывая другую проблему, с которой я столкнулся, я склонен считать, что это победитель)

Я нашел способы для них обоих:

  1. Использовать более старый g ++ ( 4.8 - по умолчанию установлен на докер )

    • export CXX=g++ (возможно, даже не обязательно)
    • г ++ ( не cmake на этот раз) не знаето cxx_std_17 , поэтому самый простой способ - удалить афферент target_compile_features (недостатком является то, что код не будет C ++ 17"совместимым")
  2. Поскольку я не знаю, как «отменить» прохождение (неисправного) пути поиска в библиотеке, я нашел обходной путь, чтобы указать правильный до it.

    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 "$0 -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 и еще несколько) на этом этапе.

...