在Linux系统中,nm 是一个非常有用的工具,用于列出目标文件中的符号。符号可以是函数名、变量名等,它们在程序开发和调试中起到关键作用。通过 nm 命令,我们可以查看二进制文件(如可执行文件或库文件)中的符号表。
nm 命令的基本语法如下:
nm [选项] 文件名
-A 或 --with-filename:显示每个符号所属的文件名。-C 或 --demangle:将低级符号名解码为更易读的形式(例如 C++ 函数名)。-D 或 --dynamic:显示动态符号表中的符号(对于共享库特别有用)。-g 或 --extern-only:仅显示外部符号。-n 或 --numeric-sort:按数值排序符号。-p 或 --no-sort:不排序符号,保持输入顺序。-S 或 --size-sort:按大小排序符号。-t format 或 --radix=format:指定地址输出格式(o 八进制, x 十六进制, d 十进制)。-u 或 --undefined-only:仅显示未定义的符号。-U 或 --defined-only:仅显示已定义的符号。假设我们有一个名为 example.o 的目标文件,可以使用以下命令查看其符号表:
nm example.o
输出可能类似于以下内容:
0000000000000004 T main
U printf
0000000000000000 b completed.7345
0000000000000000 B __bss_start
0000000000000004 b dtor_idx.7346
T:表示该符号在文本段(代码段)中。B:表示该符号在BSS段中(未初始化的全局变量)。D:表示该符号在数据段中(已初始化的全局变量)。U:表示该符号未定义(通常是外部引用)。b:表示该符号在小数据段(small data section)中。r:表示该符号在只读数据段中。创建一个名为 example.c 的文件,内容如下:
#include <stdio.h>
int global_var = 10;
void my_function() {
printf("Hello, World!\n");
}
int main() {
my_function();
return 0;
}
使用 gcc 编译器生成目标文件:
gcc -c example.c -o example.o
运行以下命令查看符号表:
nm example.o
输出可能类似于以下内容:
0000000000000004 T main
0000000000000000 T my_function
0000000000000000 B global_var
U puts
main 和 my_function 是程序中的函数,类型为 T,表示它们在代码段中。global_var 是全局变量,类型为 B,表示它在BSS段中。puts 是一个未定义符号(类型为 U),因为它是标准库中的函数。对于共享库文件(.so 文件),通常需要查看动态符号表。可以使用 -D 选项来实现:
nm -D libexample.so
如果只想查看特定类型的符号,可以结合 grep 使用。例如,只查看未定义符号:
nm example.o | grep " U "
对于C++程序,符号名可能会被编码(mangled)。使用 -C 选项可以自动解码这些符号名:
nm -C example.o