C++ linker error: Undefined reference - when linking package libtorch and shared objects together
C++ linker error: Undefined reference - when linking package libtorch and shared objects together
我是 CMake 的新手,也在尝试了解 linking 的工作原理,或者是什么导致 libtorch
和 OpenNMTTokenizer.so
无法协同工作。前者是带有CMake配置的包,后者是共享库。
如果我删除其中任何一个,二进制文件可以正常工作,但它们不能 link 在同一个项目中。也许 OpenNMTTokenizer.so
缺少 headers,但我不确定如何正确添加它们。
这是错误信息:
/usr/bin/ld: CMakeFiles/example_shared.dir/src/app.cpp.o: in function `main':
app.cpp:(.text+0x292): undefined reference to `onmt::Tokenizer::joiner_marker'
/usr/bin/ld: app.cpp:(.text+0x2a7): undefined reference to `onmt::Tokenizer::Tokenizer(onmt::Tokenizer::Mode, int, std::string const&, std::string const&, std::string const&, int)'
collect2: error: ld returned 1 exit status
该程序是一个简单的 hello world,只是为了隔离问题:
.
│
├── CMakeLists.txt
│
├── src
│ └── app.cpp
├── include
│ └── app.h.in
│ └── app.h
├── build
#
#. Project meta
#
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
project(hello_world)
configure_file(include/app.h.in include/app.h)
#
# Add libraries
#
# 1. Torch
find_package(Torch REQUIRED PATHS /usr/local/libtorch)
# Required flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
if (Torch_FOUND)
list(APPEND LINK_LIBRARIES "${TORCH_LIBRARIES}")
else()
message(FATAL_ERROR "Cannot find libtorch")
endif()
# 2. OpenNMTTokenizer
find_library(OpenNMTTokenizer lib/libOpenNMTTokenizer.so)
if (OpenNMTTokenizer)
add_library(OpenNMTTokenizer SHARED IMPORTED lib/libOpenNMTTokenizer.so)
set_target_properties(OpenNMTTokenizer
PROPERTIES
IMPORTED_LOCATION /usr/local/lib/libOpenNMTTokenizer.so
LINKER_LANGUAGE CXX)
list(APPEND LINK_LIBRARIES OpenNMTTokenizer "${OpenNMTTokenizer_LIBRARIES}")
else()
message(FATAL_ERROR "Cannot find OpenNMTTokenizer")
endif()
#
# Add the executable
#
add_executable("${PROJECT_NAME}" src/app.cpp)
set_property(TARGET "${PROJECT_NAME}" PROPERTY CXX_STANDARD 14)
target_link_libraries("${PROJECT_NAME}" "${LINK_LIBRARIES}")
target_include_directories("${PROJECT_NAME}" PUBLIC
"${CMAKE_SOURCE_DIR}/include"
"${TORCH_INCLUDE_DIRS}")
set_target_properties("${PROJECT_NAME}"
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../bin"
)
这是来源src/app.cpp
#include "app.h"
#include <iostream>
#include <vector>
#include <torch/torch.h>
#include <torch/script.h>
#include <onmt/Tokenizer.h>
using namespace onmt;
int main(){
torch::Tensor tensor = torch::eye(3);
std::cout << tensor << std::endl;
// It doesn't work if torch is linked
Tokenizer tokenizer(Tokenizer::Mode::Conservative, Tokenizer::Flags::JoinerAnnotate);
return 0;
}
完成构建输出
/usr/local/bin/cmake -S/home/inez/Projects/cmake_hello_world -B/home/inez/Projects/cmake_hello_world/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/local/bin/cmake -E cmake_progress_start /home/inez/Projects/cmake_hello_world/build/CMakeFiles /home/inez/Projects/cmake_hello_world/build//CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/inez/Projects/cmake_hello_world/build'
make -f CMakeFiles/hello_world.dir/build.make CMakeFiles/hello_world.dir/depend
make[2]: Entering directory '/home/inez/Projects/cmake_hello_world/build'
cd /home/inez/Projects/cmake_hello_world/build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/inez/Projects/cmake_hello_world /home/inez/Projects/cmake_hello_world /home/inez/Projects/cmake_hello_world/build /home/inez/Projects/cmake_hello_world/build /home/inez/Projects/cmake_hello_world/build/CMakeFiles/hello_world.dir/DependInfo.cmake --color=
Dependencies file "CMakeFiles/hello_world.dir/src/app.cpp.o.d" is newer than depends file "/home/inez/Projects/cmake_hello_world/build/CMakeFiles/hello_world.dir/compiler_depend.internal".
Consolidate compiler generated dependencies of target hello_world
make[2]: Leaving directory '/home/inez/Projects/cmake_hello_world/build'
make -f CMakeFiles/hello_world.dir/build.make CMakeFiles/hello_world.dir/build
make[2]: Entering directory '/home/inez/Projects/cmake_hello_world/build'
[ 50%] Linking CXX executable ../bin/hello_world
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/hello_world.dir/link.txt --verbose=1
/usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=0 -rdynamic CMakeFiles/hello_world.dir/src/app.cpp.o -o ../bin/hello_world -Wl,-rpath,/usr/local/libtorch/lib:/usr/local/cuda-11.2/lib64/stubs:/usr/local/cuda-11.2/lib64:/usr/local/lib /usr/local/libtorch/lib/libtorch.so /usr/local/libtorch/lib/libc10.so /usr/local/libtorch/lib/libkineto.a /usr/local/cuda-11.2/lib64/stubs/libcuda.so /usr/local/cuda-11.2/lib64/libnvrtc.so -lnvToolsExt /usr/local/cuda-11.2/lib64/libcudart.so /usr/local/libtorch/lib/libc10_cuda.so /usr/local/lib/libOpenNMTTokenizer.so -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda_cpp.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cpu.so" -Wl,--as-needed -lpthread /usr/local/libtorch/lib/libc10_cuda.so /usr/local/libtorch/lib/libc10.so /usr/local/cuda-11.2/lib64/libcufft.so /usr/local/cuda-11.2/lib64/libcurand.so /usr/local/cuda-11.2/lib64/libcublas.so /usr/local/cuda-11.2/lib64/libcudnn.so -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda_cu.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch.so" -Wl,--as-needed -lnvToolsExt /usr/local/cuda-11.2/lib64/libcudart.so
/usr/bin/ld: CMakeFiles/hello_world.dir/src/app.cpp.o: in function `main':
app.cpp:(.text+0x292): undefined reference to `onmt::Tokenizer::joiner_marker'
/usr/bin/ld: app.cpp:(.text+0x2a7): undefined reference to `onmt::Tokenizer::Tokenizer(onmt::Tokenizer::Mode, int, std::string const&, std::string const&, std::string const&, int)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/hello_world.dir/build.make:114: ../bin/hello_world] Error 1
make[2]: Leaving directory '/home/inez/Projects/cmake_hello_world/build'
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/hello_world.dir/all] Error 2
make[1]: Leaving directory '/home/inez/Projects/cmake_hello_world/build'
make: *** [Makefile:91: all] Error 2
手电筒有 TorchConfig.make
# Finds the Torch library
#
# This will define the following variables:
#
# TORCH_FOUND -- True if the system has the Torch library
# TORCH_INCLUDE_DIRS -- The include directories for torch
# TORCH_LIBRARIES -- Libraries to link against
# TORCH_CXX_FLAGS -- Additional (required) compiler flags
来自 LINK_LIBRARIES
的链接库
LINK_LIBRARIES=torchtorch_library/usr/local/libtorch/lib/libc10.so/usr/local/libtorch/lib/libkineto.a/usr/local/cuda-11.2/lib64/stubs/libcuda.so/usr/local/cuda-11.2/lib64/libnvrtc.so/usr/lib/x86_64-linux-gnu/libnvToolsExt.so/usr/local/cuda-11.2/lib64/libcudart.so/usr/local/libtorch/lib/libc10_cuda.soOpenNMTTokenizer
查看下面的答案
库被 linked 到不同的标准库。正如 @botje 所建议的那样,_GLIBCXX_USE_CXX11_ABI
的值不匹配。
我用这一行重新编译了OpenNMTTokenizer
,然后主工程编译没有报错:
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)
来自 libstdc++
If you get linker errors about undefined references to symbols that involve types in the std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are trying to link together object files that were compiled with different values for the _GLIBCXX_USE_CXX11_ABI macro.
This commonly happens when linking to a third-party library that was
compiled with an older version of GCC. If the third-party library
cannot be rebuilt with the new ABI then you will need to recompile
your code with the old ABI.
正如评论中所讨论的,事实证明其中一个组件 (OpenNMTTokenizer) 是 WITH CXX11 ABI 编译的,另外两个(Torch 和 app.cpp) 不是。这导致符号名称不匹配。
立即修复是在没有 CXX11 ABI 的情况下重新编译 OpenNMTTokenizer,尽管正确的修复是使用 CXX11 ABI 编译所有内容。这意味着重新编译 Torch。
我是 CMake 的新手,也在尝试了解 linking 的工作原理,或者是什么导致 libtorch
和 OpenNMTTokenizer.so
无法协同工作。前者是带有CMake配置的包,后者是共享库。
如果我删除其中任何一个,二进制文件可以正常工作,但它们不能 link 在同一个项目中。也许 OpenNMTTokenizer.so
缺少 headers,但我不确定如何正确添加它们。
这是错误信息:
/usr/bin/ld: CMakeFiles/example_shared.dir/src/app.cpp.o: in function `main':
app.cpp:(.text+0x292): undefined reference to `onmt::Tokenizer::joiner_marker'
/usr/bin/ld: app.cpp:(.text+0x2a7): undefined reference to `onmt::Tokenizer::Tokenizer(onmt::Tokenizer::Mode, int, std::string const&, std::string const&, std::string const&, int)'
collect2: error: ld returned 1 exit status
该程序是一个简单的 hello world,只是为了隔离问题:
.
│
├── CMakeLists.txt
│
├── src
│ └── app.cpp
├── include
│ └── app.h.in
│ └── app.h
├── build
#
#. Project meta
#
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
project(hello_world)
configure_file(include/app.h.in include/app.h)
#
# Add libraries
#
# 1. Torch
find_package(Torch REQUIRED PATHS /usr/local/libtorch)
# Required flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
if (Torch_FOUND)
list(APPEND LINK_LIBRARIES "${TORCH_LIBRARIES}")
else()
message(FATAL_ERROR "Cannot find libtorch")
endif()
# 2. OpenNMTTokenizer
find_library(OpenNMTTokenizer lib/libOpenNMTTokenizer.so)
if (OpenNMTTokenizer)
add_library(OpenNMTTokenizer SHARED IMPORTED lib/libOpenNMTTokenizer.so)
set_target_properties(OpenNMTTokenizer
PROPERTIES
IMPORTED_LOCATION /usr/local/lib/libOpenNMTTokenizer.so
LINKER_LANGUAGE CXX)
list(APPEND LINK_LIBRARIES OpenNMTTokenizer "${OpenNMTTokenizer_LIBRARIES}")
else()
message(FATAL_ERROR "Cannot find OpenNMTTokenizer")
endif()
#
# Add the executable
#
add_executable("${PROJECT_NAME}" src/app.cpp)
set_property(TARGET "${PROJECT_NAME}" PROPERTY CXX_STANDARD 14)
target_link_libraries("${PROJECT_NAME}" "${LINK_LIBRARIES}")
target_include_directories("${PROJECT_NAME}" PUBLIC
"${CMAKE_SOURCE_DIR}/include"
"${TORCH_INCLUDE_DIRS}")
set_target_properties("${PROJECT_NAME}"
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../lib"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/../bin"
)
这是来源src/app.cpp
#include "app.h"
#include <iostream>
#include <vector>
#include <torch/torch.h>
#include <torch/script.h>
#include <onmt/Tokenizer.h>
using namespace onmt;
int main(){
torch::Tensor tensor = torch::eye(3);
std::cout << tensor << std::endl;
// It doesn't work if torch is linked
Tokenizer tokenizer(Tokenizer::Mode::Conservative, Tokenizer::Flags::JoinerAnnotate);
return 0;
}
完成构建输出
/usr/local/bin/cmake -S/home/inez/Projects/cmake_hello_world -B/home/inez/Projects/cmake_hello_world/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/local/bin/cmake -E cmake_progress_start /home/inez/Projects/cmake_hello_world/build/CMakeFiles /home/inez/Projects/cmake_hello_world/build//CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/inez/Projects/cmake_hello_world/build'
make -f CMakeFiles/hello_world.dir/build.make CMakeFiles/hello_world.dir/depend
make[2]: Entering directory '/home/inez/Projects/cmake_hello_world/build'
cd /home/inez/Projects/cmake_hello_world/build && /usr/local/bin/cmake -E cmake_depends "Unix Makefiles" /home/inez/Projects/cmake_hello_world /home/inez/Projects/cmake_hello_world /home/inez/Projects/cmake_hello_world/build /home/inez/Projects/cmake_hello_world/build /home/inez/Projects/cmake_hello_world/build/CMakeFiles/hello_world.dir/DependInfo.cmake --color=
Dependencies file "CMakeFiles/hello_world.dir/src/app.cpp.o.d" is newer than depends file "/home/inez/Projects/cmake_hello_world/build/CMakeFiles/hello_world.dir/compiler_depend.internal".
Consolidate compiler generated dependencies of target hello_world
make[2]: Leaving directory '/home/inez/Projects/cmake_hello_world/build'
make -f CMakeFiles/hello_world.dir/build.make CMakeFiles/hello_world.dir/build
make[2]: Entering directory '/home/inez/Projects/cmake_hello_world/build'
[ 50%] Linking CXX executable ../bin/hello_world
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/hello_world.dir/link.txt --verbose=1
/usr/bin/c++ -D_GLIBCXX_USE_CXX11_ABI=0 -rdynamic CMakeFiles/hello_world.dir/src/app.cpp.o -o ../bin/hello_world -Wl,-rpath,/usr/local/libtorch/lib:/usr/local/cuda-11.2/lib64/stubs:/usr/local/cuda-11.2/lib64:/usr/local/lib /usr/local/libtorch/lib/libtorch.so /usr/local/libtorch/lib/libc10.so /usr/local/libtorch/lib/libkineto.a /usr/local/cuda-11.2/lib64/stubs/libcuda.so /usr/local/cuda-11.2/lib64/libnvrtc.so -lnvToolsExt /usr/local/cuda-11.2/lib64/libcudart.so /usr/local/libtorch/lib/libc10_cuda.so /usr/local/lib/libOpenNMTTokenizer.so -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda_cpp.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cpu.so" -Wl,--as-needed -lpthread /usr/local/libtorch/lib/libc10_cuda.so /usr/local/libtorch/lib/libc10.so /usr/local/cuda-11.2/lib64/libcufft.so /usr/local/cuda-11.2/lib64/libcurand.so /usr/local/cuda-11.2/lib64/libcublas.so /usr/local/cuda-11.2/lib64/libcudnn.so -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda_cu.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch.so" -Wl,--as-needed -lnvToolsExt /usr/local/cuda-11.2/lib64/libcudart.so
/usr/bin/ld: CMakeFiles/hello_world.dir/src/app.cpp.o: in function `main':
app.cpp:(.text+0x292): undefined reference to `onmt::Tokenizer::joiner_marker'
/usr/bin/ld: app.cpp:(.text+0x2a7): undefined reference to `onmt::Tokenizer::Tokenizer(onmt::Tokenizer::Mode, int, std::string const&, std::string const&, std::string const&, int)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/hello_world.dir/build.make:114: ../bin/hello_world] Error 1
make[2]: Leaving directory '/home/inez/Projects/cmake_hello_world/build'
make[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/hello_world.dir/all] Error 2
make[1]: Leaving directory '/home/inez/Projects/cmake_hello_world/build'
make: *** [Makefile:91: all] Error 2
手电筒有 TorchConfig.make
# Finds the Torch library
#
# This will define the following variables:
#
# TORCH_FOUND -- True if the system has the Torch library
# TORCH_INCLUDE_DIRS -- The include directories for torch
# TORCH_LIBRARIES -- Libraries to link against
# TORCH_CXX_FLAGS -- Additional (required) compiler flags
来自 LINK_LIBRARIES
LINK_LIBRARIES=torchtorch_library/usr/local/libtorch/lib/libc10.so/usr/local/libtorch/lib/libkineto.a/usr/local/cuda-11.2/lib64/stubs/libcuda.so/usr/local/cuda-11.2/lib64/libnvrtc.so/usr/lib/x86_64-linux-gnu/libnvToolsExt.so/usr/local/cuda-11.2/lib64/libcudart.so/usr/local/libtorch/lib/libc10_cuda.soOpenNMTTokenizer
查看下面的答案
库被 linked 到不同的标准库。正如 @botje 所建议的那样,_GLIBCXX_USE_CXX11_ABI
的值不匹配。
我用这一行重新编译了OpenNMTTokenizer
,然后主工程编译没有报错:
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)
来自 libstdc++
If you get linker errors about undefined references to symbols that involve types in the std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are trying to link together object files that were compiled with different values for the _GLIBCXX_USE_CXX11_ABI macro.
This commonly happens when linking to a third-party library that was compiled with an older version of GCC. If the third-party library cannot be rebuilt with the new ABI then you will need to recompile your code with the old ABI.
正如评论中所讨论的,事实证明其中一个组件 (OpenNMTTokenizer) 是 WITH CXX11 ABI 编译的,另外两个(Torch 和 app.cpp) 不是。这导致符号名称不匹配。
立即修复是在没有 CXX11 ABI 的情况下重新编译 OpenNMTTokenizer,尽管正确的修复是使用 CXX11 ABI 编译所有内容。这意味着重新编译 Torch。