概述
- 在Linux环境下,nm 是一个用来查看目标文件(如可执行文件、动态库或静态库)符号表的工具。
- 使用nm命令可以很方便的查看可执行程序中有哪些函数以及动态库中有哪些导出函数。
nm命令简述
打印结果含义
- 先使用nm命令查看一个可执行程序,分析下结果是什么意思
-
[root@dev nmparse]# nm res00000000004004e8 T _init0000000000600df8 t __init_array_end0000000000600df0 t __init_array_start00000000004006d8 R _IO_stdin_usedw _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable00000000004006c0 T __libc_csu_fini0000000000400650 T __libc_csu_initU __libc_start_main@@GLIBC_2.2.50000000000400621 T mainU myMathAdd00000000004005a0 t register_tm_clones0000000000400530 T _start0000000000601028 D __TMC_END__0000000000400616 T _Z6myfuncv
- 可以看到有三列数据,第一列是地址,第二列是符号类型标识符,第三列是符号名称
- 地址:即符号在内存中的地址
- 符号类型标识符
- T:全局函数
- B: 静态变量
- D: 未初始化的数据段
- U: 外部引用。比如调用的动态库函数
- 符号名称: 它代表了具体的函数名、变量名或者标签等
nm参数
- 可以通过nm --help命令查看nm的参数都有哪些,这里重点介绍常用的几个
- nm -g: 只显示全局符号,一般用来查看可执行程序中都有哪些函数
- nm -D: 只显示动态符号, 一般用来查看动态库中导出了哪些函数
- nm -C: 对C++等语言产生的低级符号名称进行解码,显示为可读的形式
查看动态库的导出函数
- 我们写两个函数,然后生成一个动态库
实现动态库
- 文件内容
- myMath.h
-
#ifndef __MY_MATH_H__#define __MY_MATH_H__int myMathAdd(int num1, int num2);int myMathMinus(int num1, int num2);#endif
- myMath.cpp
-
#include "myMath.h"int myMathAdd(int num1, int num2){return num1 + num2;}int myMathMinus(int num1, int num2){return num1 - num2;}
- 生成动态库
-
g++ -c myMath.cpp -o myMath.o -fPICg++ -shared -o libmyMath.so myMath.o
-
使用nm -D命令查看动态库的导出函数
-
nm -D libmyMath.so
- 可以看到我们写的那两个函数 myMathAdd 和 myMathMinus
-
[root@dev nmparse]# nm -D libmyMath.so0000000000201020 B __bss_startw __cxa_finalize0000000000201020 D _edata0000000000201028 B _endw __gmon_start__w _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable00000000000005bd T _Z11myMathMinusii00000000000005a9 T _Z9myMathAddii
- 但是这两个函数前后都出现了特殊符号,这是因为默认使用的C++编译器,C++是支持重载的,因此在编译出来的库,导出函数不仅包含函数名,还包含参数个数个类型。因此会出现特殊符号。
- 可以加一个 -C 参数,把特殊符号转换为可读符号。可以看到导出函数包含了参数个数和参数类型
-
[root@dev nmparse]# nm -D -C libmyMath.so0000000000201020 B __bss_startw __cxa_finalize0000000000201020 D _edata0000000000201028 B _endw __gmon_start__w _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable00000000000005ed T myMathMinus(int, int)00000000000005d9 T myMathAdd(int, int)
- 如果不想导出函数的参数个数和类型,编译动态库时可指定使用C编译器编译, 修改myMath.h如下
-
#ifndef __MY_MATH_H__#define __MY_MATH_H__extern "C" {int myMathAdd(int num1, int num2);int myMathMinus(int num1, int num2);}#endif
- 重新编译动态库后在使用nm -D命令查看,可以看到导出函数只有函数名了。
-
[root@dev nmparse]# nm -D libmyMath.so0000000000201020 B __bss_startw __cxa_finalize0000000000201020 D _edata0000000000201028 B _endw __gmon_start__w _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable00000000000005c9 T myMathAdd00000000000005dd T myMathMinus
查看可执行程序中的函数
生成可执行程序
- 写一个可执行程序,链接上面写的那个库文件
- 文件内容
-
#include "myMath.h"int dataTemp = 0;int myfunc(){return 0;}int main(){int sum = 0;sum = myMathAdd(10, 20);return 0;}
-
- 生成可执行程序并链接动态库
-
gcc main.cpp -L. -lmyMath -o res
-
使用nm -g命令查看可执行程序中的函数
-
nm -g res
- 可以看到自己实现的函数myfunc, 全局变量dataTemp以及引用的外部函数myMathAdd
-
[root@dev nmparse]# nm -g -C res0000000000601024 B __bss_start0000000000601020 D __data_start0000000000601020 W data_start0000000000601028 B dataTemp0000000000400560 T _dl_relocate_static_pie00000000004006e0 R __dso_handle0000000000601024 D _edata0000000000601030 B _end00000000004006c8 T _finiw __gmon_start__00000000004004e8 T _init00000000004006d8 R _IO_stdin_usedw _ITM_deregisterTMCloneTablew _ITM_registerTMCloneTable00000000004006c0 T __libc_csu_fini0000000000400650 T __libc_csu_initU __libc_start_main@@GLIBC_2.2.50000000000400621 T mainU myMathAdd0000000000400530 T _start0000000000601028 D __TMC_END__0000000000400616 T myfunc()