原文地址:http://t.zoukankan.com/downey-blog-p-10477835.html
1. 什么是nm
nm命令是GNU Binutils二进制工具集的一员,用于显示目标文件中的符号。如果没有为nm命令指出目标文件,则nm假定目标文件是a.out。
该命令用来列出指定文件中的符号(如常用的函数名、变量等,以及这些符号存储的区域)。
帮助手册查看:man nm
使用手册查看:man -h
目标文件、库文件、可执行文件
首先,提到这三种文件,我们不得不提的就是gcc的编译流程:预编译,编译,汇编,链接。
- 目标文件 :常说的目标文件是我们的程序文件(.c/.cpp,.h)经过预编译,编译,汇编过程生成的二进制文件,不经过链接过程,编译生成指令为:
gcc -c hello.c
- 库文件: 分为静态库和动态库,这里不做过多介绍,库文件是由多个二进制文件打包而成,生成的.a文件,示例:
ar -rsc liba.a test1.o test2.o test3.o
将test1.o test2.o test3.o三个文件打包成liba.a库文件 - 可执行文件:可执行文件是由多个二进制文件或者库文件(由上所得,库文件其实是二进制文件的集合)经过链接过程生成的一个可执行文件,对应windows下的.exe文件,可执行文件中有且仅有一个main()函数(用户程序入口,一般由bootloader指定,当然也可以改),一般情况下,二进制文件和库文件中是不包含main()函数的,但是在linux下用户有绝对的自由,做一个包含main函数的库文件也是可以使用的,但这不属于常规操作,不作讨论。
上述三种文件的格式都是二进制文件。
2. 为什么要用到nm
在上述提到的三种文件中,用编辑器是无法查看其内容的(乱码),所以当我们有这个需求(例如debug,查看内存分布的时候)去查看一个二进制文件里包含了哪些内容时,这时候就将用到一些特殊工具,linux下的nm命令就可以完全胜任(同时还有objdump和readelf工具,这里暂不作讨论)。
3. 怎么使用nm
如果你对linux下的各种概念还算了解的话,就该知道一般linux下的命令都会自带一些命令参数来满足各种应用需求,了解这些参数的使用是使用命令的开始。
man
那么,如何去了解一个命令呢,最好的方法就是linux下的man命令,linux是一个宝库,而man指令就相当于这个宝库的说明书。用法:man nm
这里面介绍了nm的各种参数以及详细用法,如果你有比较不错的英文水平和理解能力,可以直接参考man page中的内容。
nm的常用命令参数
-A 或-o或 --print-file-name:打印出每个符号属于的文件
-a或–debug-syms:打印出所有符号,包括debug符号
-B:BSD码显示
-C或–demangle[=style]:对低级符号名称进行解码,C++文件需要添加
–no-demangle:不对低级符号名称进行解码,默认参数
-D 或–dynamic:显示动态符号而不显示普通符号,一般用于动态库
-f format或–format=format:显示的形式,默认为bsd,可选为sysv和posix
-g或–extern-only:仅显示外部符号
-h或–help:国际惯例,显示命令的帮助信息
-n或-v或–numeric-sort:显示的符号以地址排序,而不是名称排序
-p或–no-sort:不对显示内容进行排序
-P或–portability:使用POSIX.2标准
-V或–version:国际管理,查看版本
–defined-only:仅显示定义的符号,这个从英文翻译过来可能会有偏差,故贴上原文:Display only defined symbols for each object file
好了,上述就是常用的命令参数,光说不练假把式,下面将给出一个示例来进一步理解nm用法:
示例代码:
#include <stdio.h>
#define MY_NUM 10
int g_mayue_num = 1;
int g_mayue_num_bk;
static int g_mayue_count = -1;
static int g_mayue_count_bk;
void mayue_print()
{
printf("g_mayue_num:%d \n", g_mayue_num);
}
int main()
{
int i_mayue_num = 1; //nm 查看不到局部变量
/* 我是一个 C 程序 */
printf("Hello, World! %d \n", MY_NUM);
mayue_print();
return 0;
}
编译指令:gcc hello.c
会生出a.out可执行文件
下面我们再来解析输出信息中各部分所代表的意思吧
- 首先,前面那一串数字,指的就是地址
- 然后,我们发现,每一个条目前面还有一个字母,类似’U’,‘B’,'D等等,其实这些符号代表的就是当前条目所对应的内存所在部分
- 最右边的就是对应的符号内容了
首要的需要讲解的就是第二点中字符所对应的含义:
同样在还是在linux命令行下man nm指令可以得到:
以上面代码为例我们定义了带mayue的变量和函数,均可以看到。
问题:
nm查看所有的函数
nm查看自定义的函数(不包含系统函数、比如printf)
nm查看定义的变量
4. 参考资料
- linux中的nm命令简介
- Linux nm 命令使用及符号含义
- nm命令中符号类型详解
- nm 命令 程序符号信息查看
- https://www.codetd.com/article/10980761
示例
nm -n myapp
对符号表中常见的变量、函数标识总结如下:(地址由底到高)
- U 引用的外部函数
- T 自定义函数(本文件内)
- t 尚未分析
- V
- D 已初始化的全局变量(如int, char, float等)
- d 已初始化的静态变量(包括全局静态变量、局部静态变量)
- B 未初始化的全局变量,存放在bss段(结构体、枚举)
- b 未初始化的static静态变量,存放在bss段(结构体、枚举)