Linux命令-nm

Posted by 周思进 on April 16, 2021

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 分别查看这两个目标文件,输出如下

image

可以看到 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 代码段了。

image image

链接了的显示如下:

image


另外 nm 还可以指定一些选项,这里介绍如下几个:

-C: 针对 C++ 阅读方便
-D: 如果查看的目标文件是动态库,使用该选项
–defined-only: 查看定义的接口
-u: 查看未定义的符号