nm 命令可以用来帮助了解一个变量、函数是在代码段、数据段,还是未初始化数据段等等。下面以如下示例代码来查看
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "test.h"
int g_var1;
int g_var2 = 1;
static int g_var3 = 2;
const char text[] = "hello";
__attribute__ ((weak)) int b;
static void fun1()
{
static int a1;
static int b1 = 1;
int c1;
int d1 = 2;
}
void fun2()
{
// nothings
}
int main()
{
char *p = malloc(10);
free(p);
if (test)
{
test();
}
printf("hello world.\n");
return 0;
}
//test.c
#include <stdio.h>
int test(void)
{
printf("call test.\n");
return 0;
}
//test.h
__attribute__ ((weak)) int test(void);
先将 main.c 和 test.c 各自生成单独的目标文件,然后通过 nm 分别查看这两个目标文件,输出如下
可以看到 nm 会输出三列,第一列是程序运行时符号所对应的地址;第二列表明符号存在于哪个段;第三列则是对应的符号名了。
下面列举第二列字母表示的含义,结合前面的代码,更容易理解;小写字母表示局部变量,大写字母表示全局变量。
B、b:表示符号位于未初始化的数据段(.bss)
C:表示未初始化的公共符号(一般全局变量可以在多个源文件中声明使用,如果没有一个源文件对其进行初始化,则最终链接阶段会明确是大B,如果有一个初始化了,则最终明确是大D,可以看后面的截图
D、d: 表示符号位于初始化的数据段(.data)
R、r 表示符号位于只读数据段(.rdata)
T、t: 表示符号位于代码段(.text)
U: 表示符号没有被定义,则对应的实现在别的目标文件中,如果最终链接都找不到,那就会出现 undefined reference to 这样的错误
V、v: 表示弱符号变量
W、w: 表示弱符号函数
可以看出来:
1、全局和局部静态变量,如果是初始化的就是在数据段,如果未初始化,则是在.bss段
2、函数则无论是静态还是非静态的,都是在 .text 段
3、函数内无论是在栈里分配的局部变量还是堆上申请的变量,nm 是无法显示出来的
4、const 声明的字符串,则是在 .rodata 段
下面生成两个执行文件,一个是没有链接 test 函数,一个是链接了 test 函数,通过 nm 查看执行文件(只截取了一部分),可以看到有链接 test 函数,最终执行文件显示的韩式 w 弱函数符号,而链接了的,则显示是 T 代码段了。
链接了的显示如下:
另外 nm 还可以指定一些选项,这里介绍如下几个:
-C: 针对 C++ 阅读方便
-D: 如果查看的目标文件是动态库,使用该选项
–defined-only: 查看定义的接口
-u: 查看未定义的符号