linux工具之nm:文件格式分析(记录一下)

一个可执行的二进制文件用nm命令可以输出如下信息,这里有个前提就是编译的时候不能去掉文件符号信息

0000000000f28100 R vendor/golang.org/x/text/unicode/norm..stmp_9
                 U vfprintf@@GLIBC_2.2.5
0000000000cf8450 T x_cgo_callers
0000000000cf8110 T x_cgo_init
000000000157c8f8 B x_cgo_inittls
0000000000cf8270 T x_cgo_mmap
0000000000cf82a0 T x_cgo_munmap
0000000000cf7f50 T x_cgo_notify_runtime_init_done
0000000000cf7f90 T x_cgo_set_context_function
0000000000cf82c0 T x_cgo_setenv
0000000000cf82f0 T x_cgo_sigaction
0000000000cf8090 T x_cgo_sys_thread_create
0000000000cf84a0 T x_cgo_thread_start
0000000000cf82e0 T x_cgo_unsetenv

其中前面是地址,中间是符号类型,最后是具体名称

符号类型如下:

符号类型说明
A该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
B该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM中
C该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B。
D该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中。
G该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。
I该符号是对另一个符号的间接引用。
N该符号是一个debugging符号。
R该符号位于只读数据区。例如定义全局const int test[] = {123, 123};则test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,test在rodata section中,大小为4。
S符号位于非初始化数据区,用于small object。
T该符号位于代码区text section。
U该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U。
V该符号是一个weak object。
W该符号是弱符号,尚未专门标记为弱对象符号。
-该符号是a.out格式文件中的stabs symbol。
?该符号类型没有定义

一个程序本质上都是由 bss段、data段、text段三个组成的。

bss段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。

    BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

data段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。

    数据段属于静态内存分配。

text段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区

    域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构

    也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量

    ,例如字符串常量等。

堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩

       减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被

       扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义

      的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,

      在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的

      返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现

      场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

一般在初始化时bss段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。
text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。

引用文章1
引用文章2

添加新评论