nm 命令

介绍

nm(英文全拼:names)命令是 GNU Binutils 二进制工具集的一员,用于显示二进制文件(例如库文件和可执行文件等)中的符号。如果没有为 nm 命令指明目标文件,则 nm 假定目标文件是 a.out。

语法

nm [选项] [文件]

选项

  • -A, -o, --print-file-name :在找到的各个符号的名字前加上文件名,而不是在此文件的所有符号前只出现文件名一次。
  • -a, --debug-syms :显示调试符号。
  • -B, --format=bsd :用来兼容 MIPS 的 nm。
  • -C, --demangle[=STYLE] :将低级符号名解码(demangle)成用户级名字,比如去除编译时添加的前置下划线,这样可以使得 C++ 函数名具有可读性。不同的编译器符号修饰风格不同,可以使用 =STYLE 参数来选择合适的解码风格。
  • -D, --dynamic :显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
  • -f, --format=FORMAT :FORMAT 可取值 bsd、sysv 或 posix,该选项在 GNU nm 中有用,默认为 bsd。
  • -g, --extern-only :仅显示外部符号。
  • -h, --help :显示帮助信息。
  • -l, --line-numbers :对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。
  • -n, -v, --numeric-sort :按符号对应地址的顺序排序,而非按符号名的字符顺序。
  • -P, --portability :使用 POSIX.2 标准输出格式代替默认的输出格式。等同于 -f posix。
  • -p, --no-sort :按目标文件中遇到的符号顺序显示,不排序。
  • -r, --reverse-sort :逆序排序。例如,升序变为降序。
  • -S, --print-size :以 BSD 输出样式输出已定义符号的值和大小。对于不记录符号大小的目标文件格式,此选项不起作用,除非使用了–size sort,在这种情况下,将显示计算的大小。
  • -s, --print-armap :当列出库中成员的符号时,同时列出索引。索引的内容包含:模块与其包含的名字的定义之间的映射。
  • -t, --radix=RADIX :使用基数 radix 进制显示符号值。radix 只能为 d(十进制)、o(八进制)或 x(十六进制)。
  • -u, --undefined-only :仅显示没有定义的符号。
  • -V, --version :显示版本信息并退出。
  • -X :为了与 AIX 版本的 nm 兼容,选项 -X 将被忽略。它可接受一个参数,该参数必须是字符串 32_64。AIX nm 的默认模式对应于 -X 32,GNU nm 不支持模式 -X 32。
  • --defined-only :仅显示有定义的符号。
  • --no-demangle :不解码低级符号名,这是默认选项。
  • --plugin NAME :加载名为 name 的插件以添加对额外目标类型的支持。只有在启用插件支持的情况下构建了工具链时,此选项才可用。
  • --size-sort :按符号大小排列。
  • --special-syms :显示目标相关的具体特殊含义的符号。这些符号通常被特定目标文件用于某些特殊处理,当包含在正常符号列表中时通常不起作用。例如,对于 ARM 目标,此选项将跳过用于标记 ARM 代码、Thumb 代码和数据之间转换的映射符号。
  • --synthetic :输出合成符号。合成符号是链接器为各种目的创建的特殊符号,默认情况下不会显示它们,因为它们不是二进制文件源代码的一部分。
  • --target=BFDNAME :指定系统默认格式以外的目标文件格式。

示例

假设 hello.c 代码如下:

#include <stdio.h>

static int g_test;
int baud_table[5] = {9600, 19200, 38400, 57600, 115200};

int main(void)
{
    int l_test;
    printf("Hello, GetIoT\n");
    return 0;
}

执行 gcc hello.c 编译后,查看 a.out 文件中所有的符号

$ nm a.out 
0000000000004010 D baud_table
0000000000004024 B __bss_start
0000000000004024 b completed.8060
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000004000 D __data_start
0000000000004000 W data_start
0000000000001090 t deregister_tm_clones
0000000000001100 t __do_global_dtors_aux
0000000000003dc0 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003dc8 d _DYNAMIC
0000000000004024 D _edata
0000000000004030 B _end
00000000000011e8 T _fini
0000000000001140 t frame_dummy
0000000000003db8 d __frame_dummy_init_array_entry
000000000000215c r __FRAME_END__
0000000000003fb8 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002014 r __GNU_EH_FRAME_HDR
0000000000004028 b g_test
0000000000001000 t _init
0000000000003dc0 d __init_array_end
0000000000003db8 d __init_array_start
0000000000002000 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
00000000000011e0 T __libc_csu_fini
0000000000001170 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000001149 T main
                 U puts@@GLIBC_2.2.5
00000000000010c0 t register_tm_clones
0000000000001060 T _start
0000000000004028 D __TMC_END__

符号说明

对于每一个符号来说,其类型如果是小写的,则表明该符号是 local 的;大写则表明该符号是 global(external)的。

  • A 该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。

  • B 该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局 static int test。则该符号 test 的类型为 b,位于 bss section 中。其值表示该符号在 bss 段中的偏移。一般而言,bss 段分配于 RAM 中。

  • C 该符号为 common。common symbol 是未初始话数据段。该符号没有包含于一个普通 section 中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个 c 文件中,定义 int test,并且该符号在别的地方会被引用,则该符号类型即为 C。否则其类型为 B。

  • D 该符号位于初始化数据段中。一般来说,分配到 data section 中。

    例如:定义全局 int baud_table[5] = {9600, 19200, 38400, 57600, 115200};,会分配到初始化数据段中。

  • G 该符号也位于初始化数据段中。主要用于 small object 提高访问 small data object 的一种方式。

  • I 该符号是对另一个符号的间接引用。

  • N 该符号是一个 debugging 符号。

  • R 该符号位于只读数据区。

    例如:定义全局 const int test[] = {123, 123};,则 test 就是一个只读数据区的符号。

  • S 符号位于非初始化数据区,用于 small object。

  • T 该符号位于代码区 text section。

  • U 该符号在当前文件中是未定义的,即该符号的定义在别的文件中。

    例如:当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为 C,在使用它的文件中,其类型为 U。

  • V 该符号是一个 weak object。

  • W 该符号是一个弱符号,没有被专门标记为 weak object 符号。

  • ? 该符号类型没有定义

Leave a Reply