U _ZN3leo6AppEnv9swimlane_B5cxx11E
0000000000661c80 B _ZN3leo6AppEnv9swimlane_E
000000000026f34c W _ZN3leo6LeoKeyaSEOS0_
000000000026f6e0 W _ZN3leo6LeoKeyC1EOS0_
000000000026e3e2 W _ZN3leo6LeoKeyC1ERKS0_
000000000032eafc T _ZN3leo6LeoKeyC1ERKSs
000000000026e1d4 W _ZN3leo6LeoKeyC1ERKSsS2_
000000000026e188 W _ZN3leo6LeoKeyC1Ev
000000000026f6e0 W _ZN3leo6LeoKeyC2EOS0_
000000000026e3e2 W _ZN3leo6LeoKeyC2ERKS0_
这些日志还不能让我们准确定位到源码, 接着继续加了 c++filt 命令来还原 C++ 编译后的函数名
c++filt(1) — Linux manual page C++ 和 Java 语言提供函数重载, 意味着您可以编写许多具有相同名称的函数, 前提是每个函数都采用不同类型的参数。 为了能够区分这些同名的 函数 C++ 和 Java 将它们编码为低级汇编程序 唯一标识每个不同版本的名称。这 过程称为重整。 c++filt 程序执行 逆映射:它将低级名称解码(解码)成 用户级别的名称,以便它们可以被读取。
此时的日志已经可以让我们定位到对应源代码, 而错误处多了关键的 [abi:cxx11] 的信息, 似乎对应了上面所说的第4点 c++ abi 版本不一致
U leo::AppEnv::swimlane_[abi:cxx11]
0000000000661c80 B leo::AppEnv::swimlane_
000000000026f34c W leo::LeoKey::operator=(leo::LeoKey&&)
000000000026f6e0 W leo::LeoKey::LeoKey(leo::LeoKey&&)
000000000026e3e2 W leo::LeoKey::LeoKey(leo::LeoKey const&)
000000000032eafc T leo::LeoKey::LeoKey(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
000000000026e1d4 W leo::LeoKey::LeoKey(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
000000000026e188 W leo::LeoKey::LeoKey()
000000000026f6e0 W leo::LeoKey::LeoKey(leo::LeoKey&&)
000000000026e3e2 W leo::LeoKey::LeoKey(leo::LeoKey const&)
背景
单元测试节点失败了, 点进来查看发现是一个内部的 Node.js C++ 插件运行时报错了 ❌, 错误信息为: undefined symbol: _ZN3leo6AppEnv9swimlane_B5cxx11E。
symbol 的基本概念
在计算机中,一个函数的指令被存放在一段内存中,当进程需要执行这个函数的时候,它必须知道要去内存的哪个地方找到这个函数,然后执行它的指令。也就是说,进程要根据这个函数的名称,找到它在内存中的地址,而这个名称与地址的映射关系,是存储在 “symbol table”中。
“symbol table”中的 symbol 就是这个函数的名称,进程会根据这个 symbol 找到它在内存中的地址,然后跳转过去执行。
问题分析
了解到 symbol 的概念后, 我们知道了 symbol 记录了变量在内存中的地址, 那么 undefined symbol 可能就是找不到该地址或者是非法不匹配的地址。
先查阅一下 undefined symbol 可能的原因 来指引一下接下来的排查方向
依赖库未找到
链接的依赖库不一致
符号被隐藏
c++ abi 版本不一致
问题排查
首先拉取出现问题的镜像开始本地复现问题, 然后使用 nm 命令来显示更多查找 symbol 时具体的信息
本地也顺利复现了该问题
接着我们运行 nm 命令来查看详细信息
下面是截取的 nm 的输出日志, 可以看到 _ZN3leo6AppEnv9swimlane_B5cxx11E 的地址是缺失的
这些日志还不能让我们准确定位到源码, 接着继续加了 c++filt 命令来还原 C++ 编译后的函数名
static std::string swimlane_;