nlist 是一个在 Unix 和类 Unix 系统(如 Linux)中,用于从目标文件(如可执行文件、目标文件、共享库等)中提取符号名的命令行工具,这些符号名通常包括变量名和函数名。

基本概念
什么是符号? 在程序编译和链接的过程中,源代码中的变量名和函数名会被转换成符号,这些符号存储在目标文件或最终的可执行文件中,符号分为几种类型:
- 定义符号: 在当前文件中定义的全局变量或函数,你在一个
.c文件中实现了一个void my_function(),my_function就是一个定义符号。 - 未定义符号: 在当前文件中引用,但定义在其他文件中的变量或函数,链接器的工作之一就是找到这些未定义符号的定义。
- 静态符号: 使用
static关键字声明的函数或变量,它们的作用域仅限于当前文件,不会暴露给链接器。
nlist 的作用
nlist 命令读取一个目标文件,并根据你提供的符号名列表,从该文件中查找这些符号的信息,如果符号存在,它会输出该符号的地址、类型等信息。
语法
nlist 命令的基本语法如下:
nlist [option] filename symbol...
参数说明:

option: 可选的命令行选项。filename: 必须参数,要检查的目标文件路径(a.out,mylib.so)。symbol...: 可选参数,一个或多个你想要查找的符号名,如果你不提供任何符号名,nlist默认会输出一个预定义的符号列表(这通常是特定于操作系统的,_edata,_end等)。
常用选项
nlist 的选项可能因 Unix 发行版(如 BSD, Solaris)而略有不同,但以下是一些常见的选项:
-g: 只列出全局符号(定义和未定义的)。-u: 只列出未定义的符号。-n: 按地址(数值)排序输出结果。-p: 不对符号名进行排序,保持文件中的原始顺序(这是默认行为)。-s: 显示符号的类型(T表示文本/代码段,D表示数据段,U表示未定义等)。-x: 以十六进制格式显示符号的地址。
输出格式
当 nlist 找到符号时,其默认输出格式通常是:
address type symbol_name
输出字段解释:
address: 符号在内存中的地址(通常是十六进制格式)。type: 符号的类型,用一个字母表示,常见的类型有:T: 文本符号,通常是一个函数。D: 数据符号,通常是一个已初始化的全局变量。B: BSS 符号,通常是一个未初始化的全局变量。U: 未定义符号,在当前文件中被引用但未定义。A: 绝对符号,地址是固定的。W: 弱符号,类似于T或D,但链接时如果没有找到强定义,也不会报错。
symbol_name: 符号的名称。
使用示例
假设我们有一个 C 程序 test.c:

// test.c
#include <stdio.h>
int global_var = 10; // 全局变量,已初始化 (D)
static int static_var = 20; // 静态变量,不对外暴露
void my_function() { // 全局函数 (T)
printf("Hello from my_function!\n");
}
int main() {
my_function();
return 0;
}
编译它:
gcc -o test test.c
我们使用 nlist 来检查 test 这个可执行文件。
示例 1:查找特定符号
查找 global_var 和 my_function。
nlist test global_var my_function
可能的输出:
0000000000404014 D global_var
0000000000401126 T my_function
解释:
global_var的地址是0x404014,类型是D(数据段)。my_function的地址是0x401126,类型是T(文本段/代码段)。
示例 2:查找不存在的符号
查找一个不存在的符号 non_existent_func。
nlist test non_existent_func
输出:
如果没有找到,nlist 可能会静默退出,或者输出一个空行,具体取决于实现。
示例 3:使用 -s 和 -x 选项
以更详细和易读的格式查看符号。
nlist -s -x test global_var my_function
可能的输出:
0x404014 D global_var
0x401126 T my_function
在这个例子中,-x 选项确保地址以十六进制显示(虽然默认通常也是),而 -s 选项明确要求显示类型。
示例 4:查找未定义符号
如果我们的程序调用了另一个库中的函数,printf,它就是一个未定义符号。
nlist -u test printf
可能的输出:
U printf
解释:
printf在test文件中被引用(在my_function内部),但它的定义在libc.so库中,因此类型是U(未定义)。
现代替代工具
虽然 nlist 是一个经典工具,但在现代 Linux 系统中,开发者更倾向于使用更强大、更通用的工具,
-
nm- 这是最常用、最推荐的替代品。
nm列出目标文件中的符号表,功能比nlist更丰富,输出格式更灵活,并且是 GNU Binutils 的一部分,在所有 Linux 系统上都可用。 - 示例:
nm test会列出test文件中的所有符号。
- 这是最常用、最推荐的替代品。
-
objdump- 这是一个极其强大的工具,用于显示一个或多个目标文件的信息,它可以显示反汇编代码、头部信息、符号表、重定位条目等。
- 要查看符号表,可以使用:
objdump -t test。
-
readelf- 专门用于分析 ELF (Executable and Linkable Format) 格式文件的工具,这是 Linux 下最常用的可执行文件格式。
- 要查看符号表,可以使用:
readelf -s test。
| 特性 | nlist |
nm |
objdump / readelf |
|---|---|---|---|
| 主要用途 | 从文件中提取特定符号的信息。 | 列出目标文件的符号表。 | 显示目标文件的详细信息(包括但不限于符号)。 |
| 优点 | 简单直接,专注于符号查找。 | 功能强大,选项丰富,是行业标准。 | 功能最全面,是二进制分析的“瑞士军刀”。 |
| 缺点 | 功能相对有限,选项较少,在现代系统上可能不被默认安装或行为略有差异。 | - | 输出信息可能过于冗杂,对于简单任务显得笨重。 |
| 推荐场景 | 遇到旧系统或脚本,需要快速查找几个已知符号。 | 日常开发和调试的首选工具。 | 深入分析二进制文件,如反汇编、检查段信息等。 |
nlist 是一个历史悠久的工具,理解它有助于了解 Unix 系统的底层工作原理,但在今天,如果你在 Linux 上工作,nm 是处理符号表更现代、更可靠的选择。
