尽管在 CMake 中指定了库,但未定义的引用错误(与 libtorch 链接的问题(C++11 ABI?)

Posted

技术标签:

【中文标题】尽管在 CMake 中指定了库,但未定义的引用错误(与 libtorch 链接的问题(C++11 ABI?)【英文标题】:Undefined reference error despite of having the library specified in CMake (issues linking with libtorch (C++11 ABI?) 【发布时间】:2021-01-08 15:18:36 【问题描述】:

我正在尝试从我制作的库中创建一个测试可执行文件。让我们将它们命名为 lib1 和 lib2。 lib1 与它的测试一起构建得很好。 lib2 的构建也没有任何问题。但是,每当我尝试将 lib2 与其测试可执行文件(即使用 lib2 的示例程序)链接时,我都会收到以下错误:

usr/bin/ld: CMakeFiles/Lib2_Test.dir/Lib2_Test.cpp.o: in function `main':
Lib2_Test.cpp:(.text+0xf3): undefined reference to `Lib2::Lib2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, int, int)'
/usr/bin/ld: Lib2_Test.cpp:(.text+0x3f5): undefined reference to `Lib2::Evaluate(bool&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, float&, cv::Mat&, cv::Mat&, bool)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/Lib2_Test.dir/build.make:130: Lib2_Test] Error 1
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/Lib2_Test.dir/all] Error 2
make: *** [Makefile:130: all] Error 2

我尝试使用readelf -dldd 命令查看标题,这两个库似乎都有所有必要的引用。但是 lib1 没有任何问题,而 lib2 在链接到使用它的可执行文件时会生成未引用的相关错误。

以下是我为它们制作的 cmakeLists,后来我还包括了readelf 的输出。

lib1 的 CMakelist.txt:

cmake_minimum_required(VERSION 3.11)
project(Lib1)

set(CMAKE_CXX_STANDARD 17)

find_package(Torch REQUIRED)
find_package(OpenCV REQUIRED)

add_definitions(-D_LIB1_BUILD_DLL) 

set( CMAKE_CXX_FLAGS  "$CMAKE_CXX_FLAGS -fPIC" )

set(CMAKE_INSTALL_PREFIX /home/me/Desktop/LibtorchPort/built_stuff)

include_directories( /home/me/Desktop/LibtorchPort/Dependencies/include $TORCH_INCLUDE_DIRS $OpenCV_INCLUDE_DIRS)
LINK_DIRECTORIES(/home/me/libtorch-cxx11-abi-shared-with-deps-1.6.0+cpu/libtorch/lib)

# http://dlib.net/examples/CMakeLists.txt.html
add_subdirectory(/home/me/dlib-19.21 /home/me/dlib-19.21/build)     

set(Lib1_SRC ./lib1.cpp)

add_library(lib1 SHARED  $Lib1_SRC)

# Link
target_link_libraries(lib1 $TORCH_LIBRARIES)
target_link_libraries(lib1 $OpenCV_LIBS)
target_link_libraries(lib1 dlib::dlib)

install(TARGETS lib1 LIBRARY DESTINATION lib)

这是lib1_test 的 CMakeList.txt:

cmake_minimum_required(VERSION 3.11)
project(Lib1_Test)
set(CMAKE_CXX_STANDARD 17)

find_package(Torch REQUIRED)
find_package(OpenCV REQUIRED)

set(CMAKE_INSTALL_PREFIX /home/me/Desktop/LibtorchPort/built_stuff)
include_directories( /home/me/Desktop/LibtorchPort/Dependencies/include $TORCH_INCLUDE_DIRS $OpenCV_INCLUDE_DIRS)
 
# Link
add_executable(lib1_dynamic_test ./Lib1_Test.cpp)
target_link_directories(lib1_dynamic_test PRIVATE /home/me/Desktop/LibtorchPort/Lib1/build)

target_link_libraries(lib1_dynamic_test lib1 )
target_link_libraries(lib1_dynamic_test $TORCH_LIBRARIES )
target_link_libraries(lib1_dynamic_test $OpenCV_LIBS)
install(TARGETS lib1_dynamic_test DESTINATION bin)

这是lib2 的 CMakeList.txt:

cmake_minimum_required(VERSION 3.11)
project(Lib2)

set(CMAKE_CXX_STANDARD 17)

find_package(Torch REQUIRED)
find_package(OpenCV REQUIRED)

add_definitions(-D_LIB2_BUILD_DLL) 

set(CMAKE_INSTALL_PREFIX /home/me/Desktop/LibtorchPort/built_stuff)

include_directories( /home/me/Desktop/LibtorchPort/Dependencies/include $TORCH_INCLUDE_DIRS $OpenCV_INCLUDE_DIRS)

LINK_DIRECTORIES(/home/me/libtorch-cxx11-abi-shared-with-deps-1.6.0+cpu/libtorch/lib)
LINK_DIRECTORIES(/home/me/Desktop/LibtorchPort/Lib1/build)

set(LIB2_SRC  ./lib2.cpp )

add_library(lib2_dynamic SHARED $LIB2_SRC )

target_link_directories(lib2_dynamic PRIVATE /home/me/Desktop/LibtorchPort/Lib1/build)

target_link_libraries(lib2_dynamic  lib1)
target_link_libraries(lib2_dynamic  $TORCH_LIBRARIES)
target_link_libraries(lib2_dynamic  $OpenCV_LIBS)

install(TARGETS lib2_dynamic LIBRARY DESTINATION lib)

最后是 lib2_test 的 CMakeList:

cmake_minimum_required(VERSION 3.11)
project(Lib2_Test)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_INSTALL_PREFIX /home/me/Desktop/LibtorchPort/built_stuff)

#find_package(Torch REQUIRED)
find_package(OpenCV REQUIRED)

include_directories( /home/me/Desktop/LibtorchPort/Dependencies/include $OpenCV_INCLUDE_DIRS)
LINK_DIRECTORIES(/home/me/Desktop/LibtorchPort/Lib1/build)

add_executable(Lib2_Test ./lib2_test.cpp)

target_link_directories(Lib2_Test PRIVATE /home/me/Desktop/LibtorchPort/Lib2/build)
#target_link_directories(Lib2_Test PUBLIC /home/me/Desktop/LibtorchPort/Lib1/build)

#Link
target_link_libraries(Lib2_Test $OpenCV_LIBS)
target_link_libraries(Lib2_Test lib2_dynamic)
install(TARGETS Lib2_Test DESTINATION bin)

运行readelf -d lib1 这是我得到的输出:

Dynamic section at offset 0x908f8 contains 38 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libtorch.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc10.so]
 0x0000000000000001 (NEEDED)             Shared library: [libtorch_cpu.so]
 0x0000000000000001 (NEEDED)             Shared library: [libopencv_highgui.so.3.4]
 0x0000000000000001 (NEEDED)             Shared library: [libopencv_imgproc.so.3.4]
 0x0000000000000001 (NEEDED)             Shared library: [libopencv_core.so.3.4]
 0x0000000000000001 (NEEDED)             Shared library: [libcblas.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [liblapack.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x000000000000000e (SONAME)             Library soname: [libLib1.so]
 0x000000000000001d (RUNPATH)            Library runpath: [/home/me/anaconda3/lib/python3.8/site-packages/torch/lib:/home/me/libtorch-cxx11-abi-shared-with-deps-1.6.0+cpu/libtorch/lib:/usr/local/lib:]
 0x000000000000000c (INIT)               0x1a000
 0x000000000000000d (FINI)               0x57e00
 0x0000000000000019 (INIT_ARRAY)         0x90b28
 0x000000000000001b (INIT_ARRAYSZ)       32 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x90b48
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x328
 0x0000000000000005 (STRTAB)             0x6840
 0x0000000000000006 (SYMTAB)             0x1758
 0x000000000000000a (STRSZ)              56053 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x92000
 0x0000000000000002 (PLTRELSZ)           8112 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x17ff0
 0x0000000000000007 (RELA)               0x14b58
 0x0000000000000008 (RELASZ)             13464 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x149f8
 0x000000006fffffff (VERNEEDNUM)         5
 0x000000006ffffff0 (VERSYM)             0x14336
 0x000000006ffffff9 (RELACOUNT)          6
 0x0000000000000000 (NULL)               0x0

这是lib2的输出:

Dynamic section at offset 0x37ba0 contains 32 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libLib1.so]
 0x0000000000000001 (NEEDED)             Shared library: [libtorch.so]
 0x0000000000000001 (NEEDED)             Shared library: [libtorch_cpu.so]
 0x0000000000000001 (NEEDED)             Shared library: [libopencv_core.so.3.4]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [libLib2_dynamic.so]
 0x000000000000001d (RUNPATH)            Library runpath: [/home/me/anaconda3/lib/python3.8/site-packages/torch/lib:/home/me/libtorch-cxx11-abi-shared-with-deps-1.6.0+cpu/libtorch/lib:/home/me/Desktop/LibtorchPort/Lib1/build:/usr/local/lib:]
 0x000000000000000c (INIT)               0x1e000
 0x000000000000000d (FINI)               0x2ec10
 0x0000000000000019 (INIT_ARRAY)         0x38108
 0x000000000000001b (INIT_ARRAYSZ)       16 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x38118
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x2f0
 0x0000000000000005 (STRTAB)             0x7d88
 0x0000000000000006 (SYMTAB)             0x1dd0
 0x000000000000000a (STRSZ)              62708 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x39000
 0x0000000000000002 (PLTRELSZ)           14784 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x19e90
 0x0000000000000007 (RELA)               0x17b38
 0x0000000000000008 (RELASZ)             9048 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x17a78
 0x000000006fffffff (VERNEEDNUM)         3
 0x000000006ffffff0 (VERSYM)             0x1727c
 0x000000006ffffff9 (RELACOUNT)          4
 0x0000000000000000 (NULL)               0x0

然而lib1 的构建和链接都很好,而依赖于lib1lib2 在链接到其测试或任何其他库时会出现问题。在这一点上我一无所知,也不知道是什么原因造成的。我错过了什么?

更新 1

这是lib2_test.cpp:https://paste.ee/p/pOgFk 这就是头文件的样子:

#ifndef Lib2_H
#define Lib2_H

/* If we are we on Windows, we want a single define for it.*/
#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__))
#define _WIN32
#endif /* _WIN32 */

#if defined(_WIN32) && defined(_Lib2_BUILD_DLL)
/* We are building Lib2 as a Win32 DLL */
#define LIB2_API __declspec(dllexport)
#elif defined(_WIN32) && defined(Lib2_DLL)
/* We are calling Lib2 as a Win32 DLL */
#define LIB2_API __declspec(dllimport)
#elif defined(__GNUC__) && defined(_Lib2_BUILD_DLL)
/* We are building Lib2 as a shared / dynamic library */
#define LIB2_API __attribute__((visibility("default")))
#else
/* We are building or calling Lib2 as a static library */
#define LIB2_API
#endif

#include <string>
#include <vector>
#include <map>
#include <memory>
#include <opencv2/core.hpp>


enum class ValidationStatus

    None = -1,
    UnderValidation = 0,
    Validated = 1,
    Invalidated = 2,
    AnomalyDetected = 3,
    ToomuchAnonalyDetected = 4
;

typedef std::tuple<ValidationStatus, std::string, bool> Lib2Result;

class Lib2Impl;

class LIB2_API Lib2

private:
    std::shared_ptr<Lib2Impl> Lib2;

public:
    Lib2(std::string shape_predictor_path = "", std::string eyeNet_path = "", int valid_presence_delay = 5, int fpasPassed = 0);

    std::vector<Lib2Result> Run(std::map<std::string, bool>& validity_result, 
                                       std::vector<std::tuple<std::string, float>>& ids,
                                       std::vector<cv::Mat>& faces, 
                                       cv::Mat& originalImage,
                                       bool show_debug_info=false);

    Lib2Result Evaluate(bool& status, std::string& name, float& theta, cv::Mat& face_image, cv::Mat& originalImage, bool debug_info = true);
    ~Lib2();

;

#endif // !Lib2_H

关于 cmets,如您所见,lib2_test.cpp 上的调用正常,并且使用相同的签名。

更新 2

我还需要补充一点,该项目是在 Windows 中使用 Visual Studio 和 cmake 构建的!然而在 Linux(ubuntu 20.04),我正面临这些问题。所以这不仅仅是调用不同/错误的方法或使用错误的签名。这段代码应该可以很好地编译和链接,但是我在这里做错了,我不确定它是什么。

注2

lib1 和 lib2 只是为实际文件名组成的名称(我只是选择 lib1 和 lib2 以使事情更简单,并在这里替换了名称,所以如果您发现大小写不同,请不要介意,实际文件是一样的。

【问题讨论】:

错误表明您在 lib2_test 中进行的调用与库中任何调用的签名都不匹配。 使用nm -C 查看liblib2_dynamic.so 中定义了哪些符号 - 特别是 Lib2::Evaluate 和 Lib2::Lib2。 如果其他 cmets 不清楚,似乎 Lib2_Test 不正确,因为它试图使用功能 Lib2::Evaluate 和构造函数 Lib2::Lib2,这可能应该作为 @ 的一部分实现987654349@。你确定这是lib1 的问题吗? 谢谢你们。非常感谢您的帮助。 @JohnZwinck 我做了,结果如下:paste.ee/p/b2Kk4 符号在那里。 @squareskittles 不介意名称,我替换了实际名称以遵循我在问题中给出的示例。他们指的是同一个文件。我还需要补充一点,整个项目是在 Visual Studio 和 windows 中创建的,一切正常。然后有人告诉我,我们也必须为 linux 提供二进制文件,我的故事从这里开始。这就是 cmake 进来的地方,我在这里 【参考方案1】:

总结

第三方库(火炬)是使用Pre-cxx11 ABI 构建的,使用该库构建的库显然无法链接到使用cxx11 ABI 的对象!

长解释

经过数小时的调试,一段代码在 Windows 中的 Visual Studio 和 CMake 中运行良好,而在 Linux 中却令人头疼,我找到了罪魁祸首!

libtorch 附带了两种类型的构建 Pre-cxx11cxx11 ABI! 它使用的是 Python 包 (torch1.6cpu) 附带的 Pre-cxx11 构建的库,并且由于 Anaconda3 在路径中,我也使用它来构建遇到这个问题的库。

在所有这些小时之后泄露出来的是未定义方法的奇怪参数:std::__cxx11::basic_string,它应该只是std::string。我没想到这两个会有所不同,我想,直到不久之前,g++ 还在使用一个奇怪的命名方案,这让我说,让我们搜索一下,也许我能找到一些东西!瞧!就是这样:

如果您收到有关未定义符号引用的链接器错误 涉及 std::__cxx11 命名空间或标签 [abi:cxx11] 中的类型 那么它可能表明您正在尝试将对象链接在一起 使用不同值编译的文件 _GLIBCXX_USE_CXX11_ABI 宏。当链接到使用旧版本 GCC 编译的第三方库时,通常会发生这种情况。如果 第三方库不能用新的 ABI 重建,那么你 将需要使用旧 ABI 重新编译您的代码。

ref

为了解决这个问题,我只是在所有库创建中直接使用了 libtorch cxx11,然后就完成了。这与 Pytorch 的官方文档中显示的不同。不要这样做:

cmake -DCMAKE_PREFIX_PATH="$(python -c 'import torch.utils; print(torch.utils.cmake_prefix_path)')" ..

如果你像我一样期望这些库是用cxx11 构建的,那么所有的地狱都会崩溃!因为他们没有!

因此,如果您在 Linux 上,只需获取并使用带有 CXX11 ABI 的预构建库!并避免使用 Pytorch 附带的内容!

注意

如果您使用 Pybind11 和 libtorch 构建 Python 扩展,请确保您的 Pytorch 也是使用 GLIBCXX_USE_CXX11_ABI=1 构建的,否则您将看到未定义的引用,原因您现在知道了!您可以通过在终端/cmd 中运行以下 sn-p 来检查这一点:

python -c "import torch; print(f'GLIBCXX_USE_CXX11_ABI = int(torch._C._GLIBCXX_USE_CXX11_ABI)')"

基于information provided here conda 包(仅限cuda 构建)应与GLIBCXX_USE_CXX11_ABI=1 一起提供。我使用 pip 和 conda 测试了 1.6cpu,但它们都报告了GLIBCXX_USE_CXX11_ABI = 0。所以请注意这一点。

如果您碰巧需要从源代码构建,可以关注this guide。

为什么 Pytorch 会这样发布:

我们设置了该标志,因为我们使用 gcc 4.9.x 构建,它只有 旧的 ABI。在 GCC 5.1 中,更改了 std::string 的 ABI,并且 使用 gcc >= 5.1 编译的二进制文件与 ABI 不兼容 除非您设置了该标志,否则使用 gcc ref

【讨论】:

以上是关于尽管在 CMake 中指定了库,但未定义的引用错误(与 libtorch 链接的问题(C++11 ABI?)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Lambda、API Gateway 和 Cloudfront 时,尽管在 Lambda 响应中指定了“Access-Control-Allow-Origin”,但 CORS 错误

C:尽管添加了 `-ldl` 标志,但未定义对 `dlopen`/`dlsym` 的引用

尽管正确链接 HTML,但未定义错误

Electron - 尽管 nodeIntegration 为真,但未定义要求

登录失败,虽然我在我的 RDS 实例的安全组中指定了入站规则

Scala 编译器中的错误:java.lang.AssertionError:断言失败(即使在 Eclipse 中指定了项目依赖项)