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

Why does changing the linking order fix some linking errors on one system?

所以我在 GitLab CI 中有这种 st运行ge 行为。我让它工作了,但现在我想知道为什么它会工作。

首先,我从 GitLab CI 开始。我在我的机器 (Arch Linux) 上得到了一个带有 docker 的本地 运行ner,这样我就可以在不推送和等待的情况下进行测试。我用 googletest 框架写了一个测试(只是一个 assert true)。我在本地触发了脚本,一切正常。所有测试都在本地 docker 图像中通过。

所以现在,当一切正常时,我推送到存储库,运行ner 接手了这项工作。 运行 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 docker镜像是:sha256:95d81930694ca9e705b71dc141ddd13f466f4989857f74aebaf1d29ba6553775

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

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

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

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

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

目标文件 (*.o) 已被消耗 "wholesale",因此订购它们不再那么麻烦。

在 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 文件中对库路径进行硬编码,因为这会再次使您的构建不可移植,并且可能会在不同的工具链上完全中断。