llvm-nm是查看llvm目标文件符号最直接可靠的方式,支持c++名称自动解码;llvm-readelf可深入查看.symtab/.dynsym及符号绑定与可见性;符号缺失常因static、-fvisibility=hidden或lto优化导致。

llvm-nm 是查看 LLVM 目标文件(如 .o)符号最直接、最可靠的方式,它比 GNU nm 更兼容 LLVM 生成的 bitcode 和现代目标格式(如 ELF、Mach-O),且默认支持 C++ 名称解码。
用 llvm-nm 查看全局函数和变量符号
编译时未加 -fvisibility=hidden 或 static 的函数/变量,会以全局符号形式出现在符号表中:
-
llvm-nm -gC your_file.o:只显示全局符号(-g),并自动解码 C++ 符号名(-C) -
llvm-nm -gC your_file.o | grep " T ":过滤出文本段定义的函数(类型T) -
llvm-nm -gC your_file.o | grep " D ":过滤出已初始化的全局变量(类型D)
注意:llvm-nm 不依赖调试信息,只要符号未被 strip 就能读取;但若用了 -fvisibility=hidden 或 static,这些符号不会出现在 -g 输出里——得去掉 -g 用 llvm-nm your_file.o 看全部符号(含本地符号 t、b 等)。
llvm-readelf 查看 ELF 符号节(更底层、更结构化)
当需要确认符号是否在 .symtab 还是 .dynsym 中,或想检查符号绑定(global/weak/local)、可见性(default/hidden)时,llvm-readelf 比 llvm-nm 更透明:
-
llvm-readelf -s your_file.o:打印完整符号表,含Bind(STB_GLOBAL/STB_LOCAL)、Vis(STV_DEFAULT/STV_HIDDEN)、Section列 -
llvm-readelf -s --dyn-syms your_file.o:仅看动态符号表(对可执行文件或共享库更有意义;普通.o文件通常为空)
关键区别:.symtab 是链接期使用的全量符号表;.dynsym 是运行期动态链接器用的子集。静态库中的 .o 文件一般只有 .symtab,没有 .dynsym。
为什么不用 objdump -t?
objdump -t 能显示符号,但它本质是 GNU Binutils 工具链的一部分,对 LLVM 生成的某些目标格式(尤其是含 LTO bitcode 或非标准重定位的 .o)支持不稳定:
- 可能报错:
objdump: unable to disassemble (unknown arch)或漏掉符号 - 不自动解码 C++ 名称,需额外加
--demangle(且解码质量不如llvm-nm -C) - 对 Mach-O(macOS)目标文件支持弱于
llvm-nm
除非你明确在 GNU 工具链环境下工作(比如用 gcc 编译 + ld 链接),否则优先用 llvm-nm 和 llvm-readelf。
符号“看不见”的常见原因
即使源码写了函数,llvm-nm -gC 也看不到,大概率是以下之一:
- 函数定义为
static或声明在匿名命名空间内 → 只在llvm-nm全量输出(无-g)中显示为t类型 - 编译时加了
-fvisibility=hidden且没显式标注__attribute__((visibility("default")))→ 符号被标记为STV_HIDDEN,-g不显示 - 函数被 LTO 全局优化掉(尤其未被调用的 inline 函数)→ 根本不进符号表
- 目标文件是 bitcode(
.bc)而非 object(.o)→llvm-nm不支持,得先用llvm-dis或llc转成汇编/目标码
真正容易被忽略的是:符号存在与否,取决于编译器前端(Clang)的代码生成决策,而不是链接器。所以查符号,必须在 .o 阶段做,而不是等最终可执行文件出来再查——那时很多符号已被丢弃或重命名。











