nm查看二进制文件的符号表

2025-04发布7次浏览

nm命令查看二进制文件的符号表

在Linux系统中,nm 是一个非常有用的工具,用于列出目标文件中的符号。符号可以是函数名、变量名等,它们在程序开发和调试中起到关键作用。通过 nm 命令,我们可以查看二进制文件(如可执行文件或库文件)中的符号表。

1. 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:表示该符号在只读数据段中。
  • 第三列:符号名称。

2. 实践步骤

步骤1:创建一个简单的C程序

创建一个名为 example.c 的文件,内容如下:

#include <stdio.h>

int global_var = 10;

void my_function() {
    printf("Hello, World!\n");
}

int main() {
    my_function();
    return 0;
}

步骤2:编译程序为目标文件

使用 gcc 编译器生成目标文件:

gcc -c example.c -o example.o

步骤3:使用nm命令查看符号表

运行以下命令查看符号表:

nm example.o

输出可能类似于以下内容:

0000000000000004 T main
0000000000000000 T my_function
0000000000000000 B global_var
                 U puts

解释:

  • mainmy_function 是程序中的函数,类型为 T,表示它们在代码段中。
  • global_var 是全局变量,类型为 B,表示它在BSS段中。
  • puts 是一个未定义符号(类型为 U),因为它是标准库中的函数。

3. 扩展知识

动态符号表

对于共享库文件(.so 文件),通常需要查看动态符号表。可以使用 -D 选项来实现:

nm -D libexample.so

符号过滤

如果只想查看特定类型的符号,可以结合 grep 使用。例如,只查看未定义符号:

nm example.o | grep " U "

解码符号名

对于C++程序,符号名可能会被编码(mangled)。使用 -C 选项可以自动解码这些符号名:

nm -C example.o