为什么更改链接顺序会修复一个系统上的某些链接错误?

时间:2018-01-08 15:33:45

标签: c++ docker cmake ld gitlab-ci-runner

所以我对GitLab CI有这种奇怪的行为。我得到了它的工作,但现在我想知道为什么它的工作。

首先,我开始使用GitLab CI。我在我的机器(Arch Linux)上有一个带有docker的本地跑步者,所以我可以在不推动和等待的情况下进行测试。我用googletest框架编写了一个测试(只是断言是真的)。我在本地触发了脚本,一切正常。所有测试都在本地docker镜像中传递。

现在,当一切正常时,我推到了存储库,一名跑步者接过了这份工作。这是在Ubuntu 16.04上运行的。现在它已编译,并在执行后抛出了一个分段错误。

我在Ubuntu系统上调试了一段时间后我切换了两个库的链接顺序:

来自:

target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
    ${QT_LIBRARIES}
    ${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a
    ${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a
    ${OpenCV_LIBRARIES}
)

要:

target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
    ${QT_LIBRARIES}
    ${OpenCV_LIBRARIES}
    ${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a
    ${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a
)

我正在使用CMake进行构建。

两台PC都运行相同版本的docker(17.12.0-ce)。

使用过的gcc码头图片是:sha256:95d81930694ca9e705b71dc141ddd13f466f4989857f74aebaf1d29ba6553775

显然这个问题是有联系的: Why does the order in which libraries are linked sometimes cause errors in GCC?

现在我的问题:当两个系统都运行一个docker容器时。为什么在这种情况下更改链接顺序可以解决问题?

2 个答案:

答案 0 :(得分:2)

依赖关系。文件顺序很重要。

链接器一次处理一个静态库,并解析缺失的符号并将其拉入正在创建的可执行文件中。

因此,如果静态库(* .a)依赖于另一个静态库,则它必须出现在满足其缺失符号的静态库之前。

对象文件(* .o)已被消费"批发",因此对它们的订购不会太麻烦。

答案 1 :(得分:1)

在CMake中对此的正确解决方法是手动调整顺序,而是正确地模拟不同目标之间的相互依赖关系。

这里的顺序依赖的确切性质是工具链依赖的(在gcc上,依赖必须在依赖于链接器命令行之前; MSVC不关心;其他工具链可以选择不同的顺序要求)。 CMake确保为给定工具链生成正确顺序的唯一方法是在CMake中明确建模依赖关系。

在您的示例中,您已建模了一个依赖项的平面列表:

target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
    ${QT_LIBRARIES}
    ${OpenCV_LIBRARIES}
    ${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a
    ${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a
)

你有一个目标${PROJECT_NAME}_test依赖于一堆库。但那确实是错的!实际上,从gmock到gtest的依赖关系,你没有告诉CMake。您需要明确地为此依赖项建模,以便CMake正常工作。由于只能在目标之间指定依赖关系,我们需要为gtest和gmock引入另外两个目标:

add_library(gtest INTERFACE)
target_link_libraries(gtest INTERFACE ${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a)
add_library(gmock INTERFACE)
target_link_libraries(gmock INTERFACE ${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a)
target_link_libraries(gmock INTERFACE gtest)   # now gmock depends on gtest

target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
    ${QT_LIBRARIES}
    ${OpenCV_LIBRARIES}
    gtest
    gmock     # order doesn't matter here;
              # you can even omit gtest completely now
)

请注意此处的target_link_libraries调用会建立从gmockgtest的依赖关系。在CMake中总是模拟这样的静态库之间的直接依赖是非常重要的,否则你会遇到像你描述的那样的问题,一旦你的构建超过一定的复杂性,它就会迅速增长。

作为旁注,尽量不要在CMake文件中对库路径进行硬编码,因为这会使您的构建变得不可移植,并且可能会在不同的工具链上完全破坏。