nm命令用于解析二进制文件符号表,-c选项解混淆c++符号,通过符号类型(如大写t/d表示全局定义、小写t/d表示局部)判断可见性与链接性,配合readelf -ws和c++filt可深入分析绑定(bind)与可见性(vis)属性。

直接看符号名本身没用,得查它在二进制文件里实际长什么样、作用域和可见性如何——nm 和 readelf 是最靠得住的两个工具。
用 nm -C 快速看函数/变量是否导出
报 undefined symbol 或 multiple definition 时,先对目标文件(.o)、静态库(.a)或可执行文件跑一遍:nm -C xxx.o。
- 第二列是符号类型:小写
t表示局部函数,大写T表示全局可链接的函数;小写d是局部数据,大写D是全局已定义数据 - 如果一个函数声明了但链接时报
undefined reference,却在所有.o里都找不到对应的大写T或D,说明它根本没被编译进去,或者被static或匿名命名空间锁死了作用域 -
nm -C不显示static函数——它们压根不会出现在符号表里,这也是为什么“写了却链接不到”的常见原因
用 readelf -Ws 查符号绑定与可见性细节
当 nm 看不出问题(比如两个同名符号类型一样但还是冲突),就得上 readelf -Ws xxx.o | c++filt。
-
readelf输出里有Bind列(GLOBAL/LOCAL)和Vis列(DEFAULT/HIDDEN),这两者共同决定符号能否跨目标文件链接 - 即使
Bind是GLOBAL,若Vis是HIDDEN(比如加了__attribute__((visibility("hidden")))),链接器也会无视它 -
c++filt必须跟在后面,否则看到的是修饰后名字(如_ZN3Foo3barEv),没法人工比对
遇到名字修饰(name mangling)看不懂怎么办
C++ 函数名被编译器改写是常态,不是 bug。别试图靠肉眼猜,而是用工具还原。
- 把报错里的符号(比如
undefined reference to 'MyClass::process(int)')复制出来,粘贴进c++filt:运行echo '_ZN7MyClass7processEi' | c++filt,就能看到原始签名 - 如果
c++filt输出乱码或不匹配,大概率是声明和定义参数类型不一致(比如int*vsconst int*),或调用约定不同(__cdeclvs__stdcall) - 模板实例化符号尤其容易踩坑:如果只在
.cpp里定义模板函数,而调用在另一个编译单元,nm根本看不到对应符号——因为根本没实例化
真正难的不是查符号,而是理解“为什么这个符号不该出现”或“为什么它没出现”。比如 static 成员变量只在头文件里声明、没在 .cpp 中定义,nm 就永远扫不到它的 D;又比如用了 inline 却把它塞进 .cpp,链接器会认为每个翻译单元都有自己的副本,然后报重复定义——这些都不是工具能自动告诉你的,得结合 ODR 和编译单元模型来判断。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











