在Mac平台下,可以使用nm命令来查看一个文件的符号表信息。nm命令行工具是附带在Xcode中的,因此在使用之前请事先安装好Xcode。
nm命令的格式大致如下:
nm [ -agnoprumxjlfPA [ s segname sectname ] ] [ - ] [ -t format ] [ [ -arch arch_flag ]... ] [ file ... ]
如果不带任何可选参数的话,使用nm命令将会列出指定文件中的所有符号,符号出现的次序默认按照字符排序。如果符号代表的是一个由Object C语言编写的函数,那么其名字将是+-[Class_name(category_name) method:name:]。
对于每一个符号,其格式是“符号值 符号类型 符号名”:
不过可以看到,nm命令也有许多的参数可选,这些参数的大致作用如下:
-a,显示所有符号,包括那些专门用来调试的符号。
-g,只显示全局符号,不显示局部符号。
-n,按照数字而不是默认的字符排序。
-p,不排序,按照符号在符号表中出现的次序显示。
-r,符号按照反序显示(默认就是按照符号名字字符排序的反序,如果带上-p参数就是按照在符号表中出现次序的反序,如果带上-n参数就是按照符号名数字排序的反序)。
-u,只显示未定义的符号。
-U,不显示未定义的符号,与-u的作用刚好相反。
-j,只显示符号的名字,而不显示符号对应的数值和类型。
-s segname sectname
只解析位于文件中segname段里sectname节内的符号。
-arch archtype OS X和iOS系统都是支持所谓的肥(Fat)文件的,也就是一个大文件中包含了分别对不同平台支持的文件。
默认情况下,nm会将其中每一个平台文件中所包含的符号都列出来。如果你只想看某一个平台文件中的符号,可以用这个选项指定。
例如,对于包含了32位armv7指令集和64位armv8指令集的iOS可执行文件来说,如果只想看表示32位armv7指令集部分的符号,可以加上-arch arm参数;而如果想看表示64位armv8指令集部分的符号,可以加上-arch arm64参数。
-x,将以16进制的形式显示符号表中所有符号每一项的值,格式如下: -A,在每一行符号的前面加上文件名。
-P,以简单格式显示每一个符号,格式如下: -t format,如果使用前面的-P参数,则符号的数值将使用16进制表示,但是可以再加上-t参数来指定用别的格式显示符号的值。主要有下面几种:
d:以10进制显示;
o:以8进制显示;
x:以16进制显示(默认)。
一般来说,在每个符号的前面,都有一个字母来表示这个符号的类型。对于每一个符号来说,其类型如果是小写字母表示的的,则说明该符号是本地(Local)的或者说是内部(Internal)的,而如果是大写字母表示的,则说明该符号是全局的,也就是可供外部(External)访问的。
一共主要有以下几种类型:
1)U,未定义符号
表示这个符号没有在本文件中定义,需要解析别的文件从而找出对应符号的定义。
例如,当前文件调用另一个文件中定义的函数或者全局变量,这个被调用的函数或全局变量在当前文件中就是未定义的。(但是,在定义它的文件中,如果是函数则对应的类型是T,而如果是全局变量则其符号类型为C)。
2)A,绝对符号
表示该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这种类型的符号常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
3)T,定义在__TEXT段__text区(代码区)中的符号
表示该符号位于代码区中,其值表示该符号在整个文件当中的所处的位置。
有点奇怪的是符号“__mh_execute_header”竟然类型也为T,算作在代码区定义的符号。
4)D,定义在__DATA段__data区中的符号
表明该符号位于初始化数据区中,其值表示该符号在整个文件当中的所处的位置。
5)B,定义在__DATA段__bss区中的符号
表明该符号位于非初始化数据区中,其值表示该符号在bss段中的偏移。
6)C,所谓的普通(Common)符号,定义在__DATA段__common区中的符号
普通符号是定义在一个未初始化数据段内的符号。该符号没有包含于一个普通的区中,只有在链接过程中才进行分配,符号的值表示该符号需要的字节数。例如在一个C文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C,否则其类型为B。
7)I,间接符号
说明这个符号是仅仅是对另一个符号的间接引用。
8)S,其它符号
定义在除前所述其它地方的符号,例如出现在__TEXT段__const区中的符号。
莽莽乾坤起纷争,江湖色变任浮沉,神魔乱舞惊天地,英雄儿女显奇能!小彭飞刀,例无虚发,快准狠;