nm
是一个非常常用、经典的工具,尤其在 C/C++、嵌入式和系统开发中经常用来查看 二进制文件的符号表。
🌟 什么是 nm
?
👉 nm
是用于列出目标文件(.o
)、静态库(.a
)、可执行文件(ELF 等)里的 符号表信息 的工具。
✅ 它可以:
- 查看全局变量、静态变量、函数的符号
- 判断符号是定义的还是引用的
- 调试链接问题(比如多重定义、未定义符号)
- 分析目标文件中函数或变量的地址
🌟 nm
输出格式
nm hello.o
输出可能是:
0000000000000000 T main
U printf
U puts
解释:
地址 | 类型 | 符号名 |
---|---|---|
0000…0000 | T | main (已定义在本文件、代码段的符号) |
空白地址 | U | printf (未定义,外部引用符号) |
空白地址 | U | puts (未定义,外部引用符号) |
🌟 符号类型常见含义
符号 | 含义 |
---|---|
T | 已定义的函数或符号(在代码段 .text 中) |
U | 外部引用(undefined),需要链接其他文件提供 |
D | 已定义的全局变量(数据段 .data ) |
B | 已定义的全局未初始化变量(BSS 段) |
R | 只读数据段(.rodata ) |
t | 静态函数(局部作用域) |
b | 静态未初始化变量(局部 BSS) |
d | 静态初始化数据(局部 data) |
W | 弱符号(weak symbol) |
🌟 常用用法
🔹 查看某个目标文件的符号:
nm foo.o
🔹 过滤某个符号:
nm foo.o | grep my_func
🔹 显示调试符号:
nm -a foo.o
🔹 按名称排序:
nm -n foo.o
🌟 例子
代码:
#include <stdio.h>
int x = 10;
static int y = 20;
void foo() {}
int main() {
foo();
printf("x = %d\n", x);
return 0;
}
编译:
gcc -c test.c
nm test.o
输出:
0000000000000000 T foo
0000000000000004 D x
U printf
0000000000000000 t main
0000000000000000 d y
解释:
foo
是全局函数,位于代码段(T)x
是全局已初始化变量(D)y
是静态变量(局部 data 段,小写 d)main
是局部函数(小写 t,因为它被标记 static)printf
是外部符号(U)
🌟 小结
💡 nm
是查看 ELF / .o / .a 文件符号表的利器:
- 调试链接错误、符号重定义/未定义
- 查看内存布局(函数、数据在哪个段)
- 分析静态变量、全局变量、弱符号情况