概述
- 在Linux环境下,nm 是一个用来查看目标文件(如可执行文件、动态库或静态库)符号表的工具。
- 使用nm命令可以很方便的查看可执行程序中有哪些函数以及动态库中有哪些导出函数。
nm命令简述
打印结果含义
- 先使用nm命令查看一个可执行程序,分析下结果是什么意思
-
[root@dev nmparse]# nm res 00000000004004e8 T _init 0000000000600df8 t __init_array_end 0000000000600df0 t __init_array_start 00000000004006d8 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 00000000004006c0 T __libc_csu_fini 0000000000400650 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 0000000000400621 T main U myMathAdd 00000000004005a0 t register_tm_clones 0000000000400530 T _start 0000000000601028 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 -fPIC g++ -shared -o libmyMath.so myMath.o
-
使用nm -D命令查看动态库的导出函数
-
nm -D libmyMath.so
- 可以看到我们写的那两个函数 myMathAdd 和 myMathMinus
-
[root@dev nmparse]# nm -D libmyMath.so 0000000000201020 B __bss_start w __cxa_finalize 0000000000201020 D _edata 0000000000201028 B _end w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 00000000000005bd T _Z11myMathMinusii 00000000000005a9 T _Z9myMathAddii
- 但是这两个函数前后都出现了特殊符号,这是因为默认使用的C++编译器,C++是支持重载的,因此在编译出来的库,导出函数不仅包含函数名,还包含参数个数个类型。因此会出现特殊符号。
- 可以加一个 -C 参数,把特殊符号转换为可读符号。可以看到导出函数包含了参数个数和参数类型
-
[root@dev nmparse]# nm -D -C libmyMath.so 0000000000201020 B __bss_start w __cxa_finalize 0000000000201020 D _edata 0000000000201028 B _end w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 00000000000005ed 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.so 0000000000201020 B __bss_start w __cxa_finalize 0000000000201020 D _edata 0000000000201028 B _end w __gmon_start__ w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 00000000000005c9 T myMathAdd 00000000000005dd 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 res 0000000000601024 B __bss_start 0000000000601020 D __data_start 0000000000601020 W data_start 0000000000601028 B dataTemp 0000000000400560 T _dl_relocate_static_pie 00000000004006e0 R __dso_handle 0000000000601024 D _edata 0000000000601030 B _end 00000000004006c8 T _fini w __gmon_start__ 00000000004004e8 T _init 00000000004006d8 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 00000000004006c0 T __libc_csu_fini 0000000000400650 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 0000000000400621 T main U myMathAdd 0000000000400530 T _start 0000000000601028 D __TMC_END__ 0000000000400616 T myfunc()