nm#

nm 是 GNU Binutils 工具集中的一个命令,用于列出目标文件(如 .o 文件、共享库 .so 文件或可执行文件)中的符号表信息。它可以帮助开发者查看文件中定义的符号、引用的符号以及符号的类型、地址等信息。

以下是 nm 指令的详细说明:


基本语法#

nm [选项] 目标文件

常用选项#

选项

说明

-a

显示所有符号,包括调试符号。

-g

仅显示外部(全局)符号。

-u

仅显示未定义的符号。

-D

显示动态符号表(适用于共享库)。

-C

将 C++ 符号名称解码为可读格式(demangle)。

-l

显示符号所在的文件名和行号(如果有调试信息)。

-n

按符号地址排序(默认按符号名称排序)。

-r

按符号地址逆序排序。

-S

显示符号的大小(如果可用)。

-t <format>

指定输出格式(d 十进制,o 八进制,x 十六进制)。

--help

显示帮助信息。

--version

显示版本信息。


符号类型#

nm 输出的符号类型由一个字母表示,常见的符号类型包括:

符号

说明

A

绝对符号(Absolute symbol)。

B

未初始化的数据段(BSS)中的符号。

C

未初始化的公共符号(Common symbol)。

D

已初始化的数据段中的符号。

G

已初始化的数据段中的全局符号。

I

间接引用的符号。

N

调试符号。

R

只读数据段中的符号。

S

未初始化的数据段中的符号。

T

文本段(代码段)中的符号(通常是函数)。

U

未定义的符号(通常需要从外部链接)。

V

弱符号(Weak symbol)。

W

未标记的弱符号。

?

未知类型的符号。


示例#

1. 查看目标文件的符号表#

nm myfile.o

输出示例:

0000000000000000 T main
0000000000000000 D global_var
                 U printf
  • T mainmain 是一个在文本段中定义的函数。

  • D global_varglobal_var 是一个在已初始化数据段中定义的全局变量。

  • U printfprintf 是一个未定义的符号,需要从外部链接。

2. 查看共享库的动态符号表#

nm -D libexample.so

输出示例:

00000000000005a0 T my_function
                 U malloc
  • T my_functionmy_function 是在共享库中定义的函数。

  • U mallocmalloc 是一个未定义的符号,需要从外部链接。

3. 解码 C++ 符号名称#

nm -C myfile.o

输出示例:

0000000000000000 T main
0000000000000000 D global_var
                 U std::cout
  • std::cout 是一个解码后的 C++ 符号名称。

4. 显示符号大小#

nm -S myfile.o

输出示例:

0000000000000000 0000000000000014 T main
0000000000000000 0000000000000004 D global_var
  • 0000000000000014main 函数的大小(20 字节)。

  • 0000000000000004global_var 变量的大小(4 字节)。


应用场景#

  1. 调试:查看目标文件或共享库中定义的符号,帮助定位问题。

  2. 链接问题:检查未定义的符号(U),确保所有符号都能正确链接。

  3. 符号分析:分析目标文件中的符号类型和大小,了解程序结构。

  4. 逆向工程:查看二进制文件中的符号信息,辅助逆向分析。


注意事项#

  • 如果目标文件是剥离了符号表的(stripped),nm 可能无法显示符号信息。

  • 对于动态库,使用 -D 选项可以查看动态符号表。

  • 对于 C++ 文件,使用 -C 选项可以解码符号名称,使其更易读。

通过 nm 命令,开发者可以深入了解目标文件的符号信息,为调试、链接和分析提供重要支持。