nm用于列出程序文件中的符号。建立nmtest.c文件:
1 #include<time.h>
2
3 int global1;
4 int global2=3;
5
6 static int static_global1;
7 static int static_global2=3;
8
9 void foo()
10 {
11 static int internal1;
12 static int internal2=3;
13 time(0);
14 }
15
16 static void bar()
17 {
18
19 }
20
21 int main(void)
22 {
23 int local1;
24 int local2=3;
25 foo();
26 return 0;
27 }
执行 gcc -g -c nmtest.c
然后
nm的第一列是指程序运行时符号在内存中的地址,它表示函数或变量的开始地址;第二列是指相应的符号放在哪个段,最后一列则是符号的名称。
第二列的信息对我们非常有用,可以让我了解在程序中所定义的一个符号是被放在程序的哪一个段的。下面列出常见字母含义(更多详情man):
上面nm的结果显示,存在地址为0的符号,此时列出的地址由于程序还没完成链接,所以是指符号在对应段中的相对偏移位置。另外,还可以看出time符号没有定义,因为它在c标准库libc.a内。由上可得结论:
无论静态变量是否初始化,程序段的分配方式都是一样的(都在数据段),初始化的静态变量会被分配到.data段中,否则分配在.bss段中;
非静态的全局变量所分配的段只与其是否初始化有关。初始化了则分配在.data段,否则分配在.bss段;
函数无论是静态还是非静态的,总是分配在.test段中,小写t表示静态函数,大写T表示非静态。
函数内的局部变量由于是分配在栈上的,所以在nm中看不到他们的身影。
man是个查询指令的强大工具~~~
执行gcc -g nmtest.c -o test
nm -n test
发现global1的变量的分配空间从前面的C变成了B,time符号从无定义变成了分配在.test段中。
nm命令:
选项/属性:
-a或--debug-syms:显示调试符号。
-B:等同于--format=bsd,用来兼容MIPS的nm。
-C或--demangle:将低级符号名解码(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
-D或--dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
-f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。
-g或--extern-only:仅显示外部符号。
-n、-v或--numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。
-p或--no-sort:按目标文件中遇到的符号顺序显示,不排序。
-P或--portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
-s或--print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。
-r或--reverse-sort:反转排序的顺序(例如,升序变为降序)。
--size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
-t radix或--radix=radix:使用radix进制显示符号值。radix只能为"d"表示十进制、"o"表示八进制或"x"表示十六进制。
--target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。
-u或--undefined-only:仅显示没有定义的符号(那些外部符号)。
-l或--line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。
-V或--version:显示nm的版本号。
--help:显示nm的任选项