在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