Linux 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 符号。
-
? 该符号类型没有定义
