动态库加载时出现 Undefined Symbol 的排查思路

本文记录加载动态库时遇到 undefined symbol 问题的排查思路

问题1

问题背景:
非英伟达镜像使用 cmake 编译 FasterTransformer

调用链

1
2
3
4
5
torch.classes.load_library(os.path.abspath(lib_path))
torch.ops.load_library(path)
ctypes.CDLL(path)
self._handle = _dlopen(self._name, mode)
OSError: xxx/libth_transformer.so: undefined symbol: _ZN17fastertransformer9TensorMapC1ESt16initializer_listISt4pairISsNS_6TensorEEE

第一步先 c++filt 看这个符号 demangle 后的是什么,得到
fastertransformer::TensorMap::TensorMap(std::initializer_list<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, fastertransformer::Tensor> >)

去代码找,发现这块代码编译后的打包路径是 libtensor.a -> libth_utils.a -> libth_transformer.so

用 nm 找下这个 TensorMap 相关的函数签名

1
2
nm -D build/lib/libth_transformer.so | grep_ZN17fastertransformer9TensorMapC1ESt16initializer_listISt4pairISsNS_6TensorEEE
> U _ZN17fastertransformer9TensorMapC1ESt16initializer_listISt4pairISsNS_6TensorEEE

U 意味着这个符号 unsolved

grep initializer_list 看下,发现一个相似的符号_ZN17fastertransformer9TensorMapC1ESt16initializer_listISt4pairINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEENS_6TensorEEE

demangle 出来是 fastertransformer::TensorMap::TensorMap(std::initializer_list<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, fastertransformer::Tensor> >)

函数签名上差了一个 __cxx11,说明是 ABI 问题,全局搜 ABI 相关的编译选项,在根目录的 CMakeLists.txt 发现这么一段

1
2
3
4
5
6
7
8
9
10
11
12
13
if(BUILD_PYT)
...
if (USE_CXX11_ABI)
set(CMAKE_CUDA_FLAGS_RELEASE "${CMAKE_CUDA_FLAGS_RELEASE} -D_GLIBCXX_USE_CXX11_ABI=1")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_GLIBCXX_USE_CXX11_ABI=1")
set(CMAKE_CUDA_FLAGS_DEBUG "${CMAKE_CUDA_FLAGS_DEBUG} -D_GLIBCXX_USE_CXX11_ABI=1")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_USE_CXX11_ABI=1")
else()
set(CMAKE_CUDA_FLAGS_RELEASE "${CMAKE_CUDA_FLAGS_RELEASE} -D_GLIBCXX_USE_CXX11_ABI=0")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_GLIBCXX_USE_CXX11_ABI=0")
set(CMAKE_CUDA_FLAGS_DEBUG "${CMAKE_CUDA_FLAGS_DEBUG} -D_GLIBCXX_USE_CXX11_ABI=0")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_USE_CXX11_ABI=0")
endif()

想到可能是没有设置 -DCMAKE_BUILD_TYPE=Release 导致的,设置完毕后问题解决

问题2

问题背景:
非英伟达镜像使用 bazel 编译 FasterTransformer

调用链

1
2
# 上层调用链同问题 1
libth_transformer.so: undefined symbol: _ZN17fastertransformer13MoeGemmRunnerI6__halfN7cutlass15integer_subbyteILi4ELb0EEEE8moe_gemmEPKS1_PKS4_S7_PS1_PlllliP11CUstream_st

demangle 出来是
fastertransformer::MoeGemmRunner<__half, cutlass::integer_subbyte<4, false> >::moe_gemm(__half const*, cutlass::integer_subbyte<4, false> const*, __half const*, __half*, long*, long, long, long, int, CUstream_st*)

排查步骤类似,区别在于 nm -D grep moe_gemm 相似的只有
fastertransformer::MoeGemmRunner<__half, __half>::moe_gemm(__half const*, __half const*, __half const*, __half*, long*, long, long, long, int, CUstream_st*)

想到编译时没有把所有数据类型的 gemm 文件都编译进去,加上后解决

思考

参考:Linux 程序 动态库 静态库依赖关系

  • 动态库依赖静态库是否会将符号加入到动态库中?
  • 动态库A依赖动态库B是否会将符号加入到动态库中?
    只会包含 A 直接调 B 的部分,并且 nm -D A 这部分也是 unsolved 的
  • FT cmake 中大部分的 add_library 都产出静态库,只有暴露给外部使用的是动态库,也有少数 add_library 产出动态库,为什么这么做
    不知道,有空补充
  • A.so 依赖 B.so 中的函数 f,在 C++ 中调用 A 的函数,执行到 f 调用时,会报什么错误
    应该是类似 shared library not found 的问题,而不是本文的 undefined symbol,换句话说本文得到的两个思路,一个是需要 check 是否相关的 src 文件被加入编译,另一个是需要 check 是否有 ABI 不一致的问题

Question: nm -D 是看动态库的,静态库符号怎么看
nm -gC -S libyourlibrary.a


动态库加载时出现 Undefined Symbol 的排查思路
https://vicety.github.io/2023/11/18/undefined-symbol排查思路/
作者
vicety
发布于
2023年11月18日
许可协议