nm - 查看二进制符号表
nm(name list)用于列出目标文件、静态库、动态库和可执行文件中的符号表。 理解符号类型字母是用好nm的关键。
# 1. 核心概念
# 1.1 什么是符号
编译器把源码编译成机器码后,函数名、全局变量名等在二进制里以**符号(symbol)**形式存在。nm 就是查看这些符号的工具。
源码: int g_value = 42;
二进制符号:
0000000100004010 D _g_value
1
2
3
2
3
# 1.2 符号类型字母(最重要)
nm 输出第二列是一个字母,表示符号类型。大小写有含义:大写 = 外部(外部可见/已定义),小写 = 内部(局部/私有)。
| 字母 | 含义 | 说明 |
|---|---|---|
| U | Undefined | 未定义——本文件引用但未实现,需要从其他库链接 |
| T / t | Text | 代码段(函数),大写外部可见,小写内部私有 |
| D / d | Data | 已初始化全局/静态变量,大写外部,小写内部 |
| B / b | BSS | 未初始化全局/静态变量,运行时自动清零 |
| S / s | Static | 特殊静态数据(如 dyld 私有段) |
| A / a | Absolute | 绝对符号(值不随链接改变) |
| C | Common | 公共符号( tentative 定义,未分配空间) |
| I | Indirect | 间接符号(stub,指向另一个符号) |
| N | Debugging 符号 | |
| R / r | Read-only | 只读数据(如字符串常量) |
| V / v | Weak | Weak 符号(可被同名强符号覆盖) |
| W / w | Weak | 另一种 Weak 符号(未定义 weak) |
| - | stabs 调试符号 |
记忆口诀:
- U = 有引用无实现(undefined)
- T = 函数(text/code)
- D/B = 变量(已初始化/未初始化)
- 大写 = 外部可见,小写 = 文件私有
# 2. 常用命令
# 2.1 基本用法
# 列出文件中所有符号
nm <binary>
# 只看 undefined 符号(找未链接的依赖)
nm -u <binary>
# 只看已定义符号
nm -j <binary> # 只输出符号名,不带地址和类型
# 按符号名排序
nm -n <binary> # 按地址排序
nm -p <binary> # 不排序(保持文件顺序)
# 反向(不区分大小写)
nm -g <binary> # 只看外部(global)符号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2.2 iOS / macOS 常用
# 查看动态库导出了哪些符号
nm -gU /path/to/framework/Framework
# -g: 只看外部符号
# -U: 不显示 undefined(即只看已导出的)
# 查看可执行文件引用了哪些外部符号
nm -u /path/to/executable
# 查看某个类/方法的符号(Objective-C)
nm <binary> | grep -i "ViewController"
# Swift 符号 mangled 后很长,需要 demangle
nm <binary> | grep "ViewController" | swift demangle
# 批量 demangle
nm <binary> | swift demangle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.3 完整选项速查
| 选项 | 作用 |
|---|---|
-a | 显示所有符号,包括调试符号 |
-g | 只显示外部(global)符号 |
-u | 只显示未定义符号(undefined) |
-U | 只显示已定义符号 |
-j | 只输出符号名 |
-n | 按地址数值排序 |
-p | 不排序 |
-r | 逆序 |
-l | 显示行号(需要调试信息) |
-arch <arch> | 指定架构(如 arm64) |
-f | 显示 fat binary 的所有架构 |
-m | 输出 Mach-O 风格信息 |
# 3. 实际场景
# 3.1 链接报错:undefined symbol
编译时遇到 Undefined symbols for architecture arm64::
# 看可执行文件缺哪些符号
nm -u MyApp | grep <缺失符号名>
# 确认某个库是否导出了这个符号
nm -gU libSomeLib.a | grep <符号名>
1
2
3
4
5
2
3
4
5
# 3.2 查 Framework 导出了什么
nm -gU MyFramework.framework/MyFramework
1
输出示例:
0000000000003a40 T _$s11MyFramework0A5ClassC0A10MethodyyF
0000000000003b20 S _OBJC_CLASS_$_MyFramework
1
2
2
# 3.3 Swift 符号 demangle
Swift 编译后的符号是 mangled 的($s...),直接看很难懂:
# 方法1:管道
nm MyBinary | grep "MyClass" | swift demangle
# 方法2:直接 demangle
xcrun swift-demangle < mangled_symbol
1
2
3
4
5
2
3
4
5
mangled: $s11MyFramework0A5ClassC0A10MethodyyF
demangled: MyFramework.MyClass.method() -> ()
# 3.4 对比两个版本的符号差异
# 导出符号列表
nm -gU old.app/MyApp > old_symbols.txt
nm -gU new.app/MyApp > new_symbols.txt
# 对比
diff old_symbols.txt new_symbols.txt
1
2
3
4
5
6
2
3
4
5
6
# 3.5 查私有 API / 隐藏符号
# 查看系统 Framework 的所有符号(包括私有的)
nm /System/Library/Frameworks/UIKit.framework/UIKit
# 找某个类的所有方法
nm -gU UIKit | grep -i "PrivateClass"
1
2
3
4
5
2
3
4
5
# 4. 与相关工具配合
| 工具 | 用途 | 配合 nm |
|---|---|---|
otool -L | 查看链接的动态库 | nm -u 看缺什么符号,otool -L 看依赖哪些库 |
strings | 提取字符串 | nm 找符号,strings 找硬编码文本 |
lldb | 动态调试 | nm 找符号地址,lldb 在该地址下断点 |
swift-demangle | Swift 符号反混淆 | nm 输出管道给 swift-demangle |
objdump | 反汇编 | nm 定位符号,objdump -d 反汇编该符号 |
# 5. 常见问题
# 5.1 输出太多怎么办
# 只看外部已定义符号(过滤掉大量内部符号)
nm -gU <binary> | wc -l
# grep 过滤
nm <binary> | grep "T _" | grep "ViewController"
1
2
3
4
5
2
3
4
5
# 5.2 Fat Binary 多架构
# 查看所有架构
nm -f <binary>
# 指定 arm64
nm -arch arm64 <binary>
1
2
3
4
5
2
3
4
5
# 5.3 静态库 vs 动态库
- 静态库(.a):
nm lib.a会列出每个目标文件的符号,可能有大量重复 - 动态库(.dylib/.framework):
nm直接看导出/导入表
# 6. 速查
# 我想知道这个文件导出了什么
nm -gU <binary>
# 我想知道缺什么符号
nm -u <binary>
# 我想找某个函数
nm <binary> | grep "funcName"
# Swift 符号看不懂
nm <binary> | swift demangle
# 对比两个版本
diff <(nm -gU old) <(nm -gU new)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
上次更新: 2026/10/09, 15:51:11