nm 默认不显示动态库导出的 C++ 函数名,因 C++ 名字修饰和符号可见性限制;应使用 nm -D -C 或 objdump -T 查动态符号表以获取真实可调用符号。

nm 查看动态库导出的函数符号时为什么看不到 C++ 函数名
直接对 .so 文件运行 nm libxxx.so,通常看到的是乱码(如 _Z1fvi)或根本没函数——这是因为 C++ 编译器做了名字修饰(name mangling),且默认 nm 只显示未定义符号或静态符号。动态库真正导出的、可供外部调用的符号,必须满足两个条件:已定义 + 全局可见 + 未被隐藏(即未加 __attribute__((visibility("hidden"))))。nm 默认不筛选这些,所以结果不可靠。
正确命令:用 nm -D 或 objdump -T 查动态符号表
-D 参数让 nm 只读取动态符号表(.dynsym),这才是运行时动态链接器实际查找的符号来源。配合 -C(demangle)可还原为可读的 C++ 函数名:
nm -D -C libexample.so
等价但更直观的替代命令是:
objdump -T libexample.so | c++filt
常见组合建议:
立即学习“C++免费学习笔记(深入)”;
- 只看全局函数符号(排除数据符号):
nm -D -C --defined-only libexample.so | grep " T " - 过滤特定函数名(注意 demangle 后匹配):
nm -D -C libexample.so | grep "MyClass::doWork" - 确认符号是否真的可被外部链接:
readelf -d libexample.so | grep NEEDED看依赖,再结合nm -D验证导出
为什么 nm -g 不够用
-g 仅表示“全局符号”,但它从所有符号表(包括 .symtab)中提取,而 .symtab 在 strip 后会被删掉,且包含大量调试/内部符号;更重要的是,动态链接器根本不看 .symtab。所以即使 nm -g 能列出某个函数,也不能说明它能被 dlopen/dlsym 正确加载。实际生效的只有 .dynsym 中的条目,必须用 -D。
典型误判场景:
- 函数声明为
static或加了visibility("hidden")→ 不会进入 .dynsym,nm -D完全不显示 - 头文件里写了 inline 但没加
extern inline→ 编译器可能不生成外部符号 - 使用了版本脚本(.map 文件)但未导出该符号 → 即使定义了,
nm -D也不出现
验证导出是否生效的最简方式
别只信 nm 输出,用 dlsym 直接测试最可靠:
void* handle = dlopen("./libexample.so", RTLD_LAZY);<br>if (!handle) { fprintf(stderr, "%s\n", dlerror()); return; }<br>auto func = (int(*)(int)) dlsym(handle, "_Z1fvi"); // mangling 名<br>// 或用 demangled 名(需确保 dlsym 支持,一般不支持,所以仍得用 mangled)<br>dlclose(handle);关键点:
-
dlsym查找的是 mangled 名,不是nm -C显示的可读名 - 如果
nm -D libexample.so里压根没出现那个 mangled 名,dlsym必定失败 - 编译动态库时加
-fvisibility=hidden后,必须显式用__attribute__((visibility("default")))标记要导出的函数,否则nm -D为空
名字修饰和符号可见性控制是 C++ 动态库最容易漏检的两层过滤,跳过任一环节都会导致“函数写了却调不到”。


















