一般来说, 搞linux开发的人, 才会用到nm命令, 非开发的人, 应该用不到。 虽然nm很简单, 但是还是有必要写几句, 聊表心意。

nm不是ni ma的缩写, 当然, 也不是ni mei的缩写, 而是names的缩写, nm命令主要是用来列出某些文件中的符号(说白了就是一些函数全局变量等)。 下面, 我们一起来看看。

test.h为:

void print();

test.c为:

#include <stdio.h>
#include "test.h"

void print()
{
printf("rainy days\n");
}

main.c为:

#include "test.h"

int main()
{
print();
return 0;
}
好, 我们看看nm命令的作用效果, 如下:

[taoge@localhost learn_nm]$ nm *
nm: main.c: File format not recognized
nm: test.c: File format not recognized
nm: test.h: File format not recognized
[taoge@localhost learn_nm]$
ni ma, 啥都没有, 这说明nm对这类文件无用。


继续看nm能否读取目标文件和可执行文件:

[taoge@localhost learn_nm]$ ls
main.c test.c test.h
[taoge@localhost learn_nm]$ gcc -c test.c main.c
[taoge@localhost learn_nm]$ gcc test.o main.o
[taoge@localhost learn_nm]$ ./a.out
rainy days
[taoge@localhost learn_nm]$ nm *

a.out:
08049564 d _DYNAMIC
08049630 d _GLOBAL_OFFSET_TABLE_
0804849c R _IO_stdin_used
w _Jv_RegisterClasses
08049554 d __CTOR_END__
08049550 d __CTOR_LIST__
0804955c D __DTOR_END__
08049558 d __DTOR_LIST__
0804854c r __FRAME_END__
08049560 d __JCR_END__
08049560 d __JCR_LIST__
0804964c A __bss_start
08049648 D __data_start
08048450 t __do_global_ctors_aux
08048330 t __do_global_dtors_aux
080484a0 R __dso_handle
w __gmon_start__
0804844a T __i686.get_pc_thunk.bx
08049550 d __init_array_end
08049550 d __init_array_start
080483e0 T __libc_csu_fini
080483f0 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804964c A _edata
08049654 A _end
0804847c T _fini
08048498 R _fp_hw
08048290 T _init
08048300 T _start
0804964c b completed.5963
08049648 W data_start
08049650 b dtor_idx.5965
08048390 t frame_dummy
080483c8 T main
080483b4 T print
U puts@@GLIBC_2.0
nm: main.c: File format not recognized

main.o:
00000000 T main
U print
nm: test.c: File format not recognized
nm: test.h: File format not recognized

test.o:
00000000 T print
U puts
[taoge@localhost learn_nm]$
可以看到, 对于目标文件和可执行文件而言, 均可以获得其中的函数, 如print函数。


我们继续看静态库和动态库, 如下:

[taoge@localhost learn_nm]$ ls
main.c test.c test.h
[taoge@localhost learn_nm]$ gcc -c test.c
[taoge@localhost learn_nm]$ ar rcs libtest.a test.o
[taoge@localhost learn_nm]$ gcc -shared -fPIC -o libtest.so test.o
[taoge@localhost learn_nm]$ ls
libtest.a libtest.so main.c test.c test.h test.o
[taoge@localhost learn_nm]$ nm lib*

libtest.a:

test.o:
00000000 T print
U puts

libtest.so:
000014bc a _DYNAMIC
00001590 a _GLOBAL_OFFSET_TABLE_
w _Jv_RegisterClasses
000014a8 d __CTOR_END__
000014a4 d __CTOR_LIST__
000014b0 d __DTOR_END__
000014ac d __DTOR_LIST__
000004a0 r __FRAME_END__
000014b4 d __JCR_END__
000014b4 d __JCR_LIST__
000015a4 A __bss_start
w __cxa_finalize@@GLIBC_2.1.3
00000440 t __do_global_ctors_aux
00000350 t __do_global_dtors_aux
000014b8 d __dso_handle
w __gmon_start__
00000419 t __i686.get_pc_thunk.bx
000015a4 A _edata
000015ac A _end
00000478 T _fini
000002ec T _init
000015a4 b completed.5963
000015a8 b dtor_idx.5965
000003e0 t frame_dummy
00000420 T print
U puts@@GLIBC_2.0
[taoge@localhost learn_nm]$
可以看到, 我们可以从静态库和动态库中获取到函数名称, 如print函数。



好, 我们再来看看全局变量的情形, 我们把main.c改为:

#include <stdio.h>

int add(int x, int y)
{
return x + y;
}

int aaa;
int bbb = 1;
char szTest[] = "good";

int main()
{
int ccc = 2;
return 0;
}
然后用nm分析a.out(注意, 如果只有nm命令, 则默认a.out为其要处理的文件):

[taoge@localhost learn_nm]$ ls
main.c
[taoge@localhost learn_nm]$ gcc main.c
[taoge@localhost learn_nm]$ ./a.out
[taoge@localhost learn_nm]$ nm a.out
08049538 d _DYNAMIC
08049604 d _GLOBAL_OFFSET_TABLE_
0804847c R _IO_stdin_used
w _Jv_RegisterClasses
08049528 d __CTOR_END__
08049524 d __CTOR_LIST__
08049530 D __DTOR_END__
0804952c d __DTOR_LIST__
08048520 r __FRAME_END__
08049534 d __JCR_END__
08049534 d __JCR_LIST__
08049628 A __bss_start
08049618 D __data_start
08048430 t __do_global_ctors_aux
08048310 t __do_global_dtors_aux
08048480 R __dso_handle
w __gmon_start__
0804842a T __i686.get_pc_thunk.bx
08049524 d __init_array_end
08049524 d __init_array_start
080483c0 T __libc_csu_fini
080483d0 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
08049628 A _edata
08049634 A _end
0804845c T _fini
08048478 R _fp_hw
08048274 T _init
080482e0 T _start
08049630 B aaa
08048394 T add
0804961c D bbb
08049628 b completed.5963
08049618 W data_start
0804962c b dtor_idx.5965
08048370 t frame_dummy
080483a2 T main
08049620 D szTest
[taoge@localhost learn_nm]$
可以看到, 不仅有add函数, 还有全局变量aaa, bbb和szTest, 要注意, aaa是未初始化的, 所以在Bss段, 而bbb、szTest是初始化了的, 所以在Data段。 值得注意的是, 并没有ccc, 因为ccc是局部变量, nm看不到的。

我们还应该注意到, 在上面看不到"good", 为啥呢? 因为nm是用来看szTest而非"good"的。 别忘了, 我们之前介绍过的strings命令可干这事, 如下:

[taoge@localhost learn_nm]$ ls
a.out main.c
[taoge@localhost learn_nm]$ strings a.out
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
__libc_start_main
GLIBC_2.0
PTRh
[^_]
good
[taoge@localhost learn_nm]$


nm命令主要列出特性文件中的符号信息, 具体更加详细的用法, 请问man, 我就不再过多介绍了。









更多相关文章

  1. Linux下cfsetospeed和cfsetispeed函数
  2. [Linux学习]关机命令
  3. 一些常用的Linux命令
  4. Awk 命令学习总结、AWk命令系列学习(linux shell)
  5. Linux 性能查看命令:
  6. LINUX使用ps命令获取对应PID
  7. linux包之nc之nc命令
  8. 黑客常用 Linux 入侵常用命令
  9. linux使用rz和sz命令,实现小文件上传下载

随机推荐

  1. Android(安卓)Permission中英对照
  2. 手机和平板之外——带你理解跨设备的Andr
  3. 使用MediaRecorder录制音频
  4. Android实现游戏摇杆的源代码
  5. Android (SQLite 数据库与ContentProvide
  6. Android NDK相关的库方法
  7. Android shape使用和属性含义
  8. EditText的错误信息
  9. 【Android学习笔记】Android概述与开发环
  10. Android KitKat 4.4平台开发-添加USB ADB