运行时未定义的符号
Undefined Symbol at Runtime
我们在 Ubuntu 18.04 上使用 CMake 3.18 编译和 link 我们的代码。程序的结构是这样的:
有一个具有主要功能的应用程序,它在运行时加载共享库,比如说 lib_a.so。在运行时,这个库加载另外两个库,lib_b.so 和 lib_c.so 并使用它们中的符号。 lib_b.so 也使用 lib_c.so 中的符号。
lib_b.so 和 lib_c.so 是从 A 的子模块 B 和 C 编译而来的。
在某些系统上代码运行良好。然而,在某些系统上,我们会遇到未定义的符号错误,当使用 c++filt 对无法找到的符号进行 demangled 时,它看起来像这样:
Unable to load lib_a.so : lib_b.so: undefined symbol: D::E::Get(std::initializer_list<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >)
命名空间 D 和 class D::E 位于 C 的子模块下。该子模块不包含 CMakeLists.txt,并且不会从中生成共享库或静态库。它是一个目录,仅包含源文件和头文件。
当我们使用 nm --defined-only lib_c.so 查看 lib_c.so 的定义符号时,我们看到有这个符号:
D::E::Get(std::initializer_list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)
我们的G++版本是7.5.0,CMake版本是3.18.0。
据此,我们推断这是一个 CXX11 ABI 问题。所以,这些是我们分别尝试过的东西:
在编译的CMakeLists.txt和links [=48]定义目标之前添加add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=1) =].
在编译的CMakeLists.txt和links lib_a.so定义目标之前,添加set(CMAKE_CXX_STANDARD11)。由于这是顶级模块,我们认为这会将其所有子模块的 c++ 标准设置为 11。
添加target_compile_features(target PUBLIC cxx_std_11),在定义target之前,编译的CMakeLists.txt和link s lib_b.so.
None 解决了问题。另外,没有变化。 lib_b.so 再次查找相同的符号。
然而,当我们编写一个 Makefile 来编译源代码和 link 共享库时,问题就消失了。
原来这是一个 third-party 库问题。
正如@Someprogrammerdude 所建议的,带有 VERBOSE=1 的 运行 CMake 显示 lib_b.so 与 -D_GLIBCXX_USE_CXX11_ABI=0.
相关联
我在B/CMakeFiles/B.dir/flags.cmake中手动修改为-D_GLIBCXX_USE_CXX11_ABI=1。这次之前的错误没有了。
然而,关于我们使用的 third-party 库之一,这给了另一个未定义的符号错误 cxx11_abi。
将库更新为使用 CXX11 ABI 编译的版本解决了该问题。
我们在 Ubuntu 18.04 上使用 CMake 3.18 编译和 link 我们的代码。程序的结构是这样的:
有一个具有主要功能的应用程序,它在运行时加载共享库,比如说 lib_a.so。在运行时,这个库加载另外两个库,lib_b.so 和 lib_c.so 并使用它们中的符号。 lib_b.so 也使用 lib_c.so 中的符号。
lib_b.so 和 lib_c.so 是从 A 的子模块 B 和 C 编译而来的。
在某些系统上代码运行良好。然而,在某些系统上,我们会遇到未定义的符号错误,当使用 c++filt 对无法找到的符号进行 demangled 时,它看起来像这样:
Unable to load lib_a.so : lib_b.so: undefined symbol: D::E::Get(std::initializer_list<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >)
命名空间 D 和 class D::E 位于 C 的子模块下。该子模块不包含 CMakeLists.txt,并且不会从中生成共享库或静态库。它是一个目录,仅包含源文件和头文件。
当我们使用 nm --defined-only lib_c.so 查看 lib_c.so 的定义符号时,我们看到有这个符号:
D::E::Get(std::initializer_list<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)
我们的G++版本是7.5.0,CMake版本是3.18.0。
据此,我们推断这是一个 CXX11 ABI 问题。所以,这些是我们分别尝试过的东西:
在编译的CMakeLists.txt和links [=48]定义目标之前添加add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=1) =].
在编译的CMakeLists.txt和links lib_a.so定义目标之前,添加set(CMAKE_CXX_STANDARD11)。由于这是顶级模块,我们认为这会将其所有子模块的 c++ 标准设置为 11。
添加target_compile_features(target PUBLIC cxx_std_11),在定义target之前,编译的CMakeLists.txt和link s lib_b.so.
None 解决了问题。另外,没有变化。 lib_b.so 再次查找相同的符号。
然而,当我们编写一个 Makefile 来编译源代码和 link 共享库时,问题就消失了。
原来这是一个 third-party 库问题。
正如@Someprogrammerdude 所建议的,带有 VERBOSE=1 的 运行 CMake 显示 lib_b.so 与 -D_GLIBCXX_USE_CXX11_ABI=0.
相关联我在B/CMakeFiles/B.dir/flags.cmake中手动修改为-D_GLIBCXX_USE_CXX11_ABI=1。这次之前的错误没有了。
然而,关于我们使用的 third-party 库之一,这给了另一个未定义的符号错误 cxx11_abi。
将库更新为使用 CXX11 ABI 编译的版本解决了该问题。