计算机中的符号是什么意思?
本文探讨了计算机编程中的符号概念及其在调试中的作用。通过分析一个简单的"hello-world"程序生成的ELF文件,作者展示了如何使用nm、readelf等工具查看符号表。符号包含三个关键特征:名称(存储在字符串节中的偏移地址)、值(内存地址)和类型(如T、D等)。文章详细介绍了静态符号和动态符号的区别,动态符号(如puts和__libc_start_main)在加载时由lo
author: hjjdebug
date: 2026年 01月 08日 星期四 12:45:41 CST
descrip: 计算机中的符号是什么意思?
文章目录
符号, 符号化调试, 计算机编译,连接,调试离不开符号,到底什么是符号呢?
甲. 什么叫符号?
研究符号, 也应该从hello-world 开始, 这里的符号,就是elf文件中的符号
0 下面是测试代码
$ cat test.c
#include "stdio.h"
int main()
{
printf("hello\n");
return 0;
}
把它编译成test 可执行文件
$ gcc -g -no-pie -o test test.c
对于名叫test 的elf 文件,
1 用 $ nm test 可列出其所有符号,非常简明
$ nm test
0000000000601030 B __bss_start
0000000000601030 b completed.7698
0000000000601020 D __data_start
0000000000601020 W data_start
0000000000400440 t deregister_tm_clones
0000000000400430 T _dl_relocate_static_pie
00000000004004b0 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000601028 D __dso_handle
0000000000600e20 d _DYNAMIC
0000000000601030 D _edata
0000000000601038 B _end
0000000000400574 T _fini
00000000004004e0 t frame_dummy
0000000000600e10 t __frame_dummy_init_array_entry
00000000004006c4 r __FRAME_END__
0000000000601000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
000000000040058c r __GNU_EH_FRAME_HDR
00000000004003c8 T _init
0000000000600e18 t __init_array_end
0000000000600e10 t __init_array_start
0000000000400580 R _IO_stdin_used
0000000000400570 T __libc_csu_fini
0000000000400500 T __libc_csu_init
U __libc_start_main@@GLIBC_2.2.5
00000000004004e7 T main
U puts@@GLIBC_2.2.5
0000000000400470 t register_tm_clones
0000000000400400 T _start
0000000000601030 D __TMC_END__
一下子多出来这么多符号, 其实从源代码中,我们只知道main 符号 和 printf 符号,
printf 在这里被puts@@GLIBC_2.2.5 替代了.
其它的先不管了.
由此看出符号的几个特点:
-
符号首先要有名字,用以区别是这个符号还是哪个符号
计算机中用一个4字节数表示.
为什么是4字节?
因为所有的名字字符串构成一个字符串节,排列在一起,这个4字节32位数,
表示这个名字的首个字符在字符串节中的偏移位置, 4字节足够使用了 -
符号要有值. 这是符号存在的意义.
例如:
00000000004004e7 T main
其值,在64bits 机器上占用8bytes 地址值,也足够大了 -
符号有类型. 例如这里的T,t,D,d,w,B,R 等等
2. 用 $ readelf -s test 有对符号更细致的描述.
它会列出动态符号与静态符号集合.
3. 用 $ readelf --dyn-syms test 会只列出动态符号
$ readelf --dyn-syms test
Symbol table '.dynsym' contains 4 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
关注 puts(即转义的printf), 及__libc_start_main
动态符号, 其值暂时为0, 将来由loader 去确定符号具体的数值即内存偏移地址.
静态符号太长 66项,
Symbol table '.symtab' contains 66 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400238 0 SECTION LOCAL DEFAULT 1
2: 0000000000400254 0 SECTION LOCAL DEFAULT 2
3: 0000000000400274 0 SECTION LOCAL DEFAULT 3
4: 0000000000400298 0 SECTION LOCAL DEFAULT 4
5: 00000000004002b8 0 SECTION LOCAL DEFAULT 5
6: 0000000000400318 0 SECTION LOCAL DEFAULT 6
7: 0000000000400356 0 SECTION LOCAL DEFAULT 7
8: 0000000000400360 0 SECTION LOCAL DEFAULT 8
9: 0000000000400380 0 SECTION LOCAL DEFAULT 9
10: 00000000004003b0 0 SECTION LOCAL DEFAULT 10
11: 00000000004003c8 0 SECTION LOCAL DEFAULT 11
12: 00000000004003e0 0 SECTION LOCAL DEFAULT 12
13: 0000000000400400 0 SECTION LOCAL DEFAULT 13
14: 0000000000400574 0 SECTION LOCAL DEFAULT 14
15: 0000000000400580 0 SECTION LOCAL DEFAULT 15
16: 000000000040058c 0 SECTION LOCAL DEFAULT 16
17: 00000000004005c8 0 SECTION LOCAL DEFAULT 17
18: 0000000000600e10 0 SECTION LOCAL DEFAULT 18
19: 0000000000600e18 0 SECTION LOCAL DEFAULT 19
20: 0000000000600e20 0 SECTION LOCAL DEFAULT 20
21: 0000000000600ff0 0 SECTION LOCAL DEFAULT 21
22: 0000000000601000 0 SECTION LOCAL DEFAULT 22
23: 0000000000601020 0 SECTION LOCAL DEFAULT 23
24: 0000000000601030 0 SECTION LOCAL DEFAULT 24
25: 0000000000000000 0 SECTION LOCAL DEFAULT 25
26: 0000000000000000 0 SECTION LOCAL DEFAULT 26
27: 0000000000000000 0 SECTION LOCAL DEFAULT 27
28: 0000000000000000 0 SECTION LOCAL DEFAULT 28
29: 0000000000000000 0 SECTION LOCAL DEFAULT 29
30: 0000000000000000 0 SECTION LOCAL DEFAULT 30
31: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
32: 0000000000400440 0 FUNC LOCAL DEFAULT 13 deregister_tm_clones
33: 0000000000400470 0 FUNC LOCAL DEFAULT 13 register_tm_clones
34: 00000000004004b0 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
35: 0000000000601030 1 OBJECT LOCAL DEFAULT 24 completed.7698
36: 0000000000600e18 0 OBJECT LOCAL DEFAULT 19 __do_global_dtors_aux_fin
37: 00000000004004e0 0 FUNC LOCAL DEFAULT 13 frame_dummy
38: 0000000000600e10 0 OBJECT LOCAL DEFAULT 18 __frame_dummy_init_array_
39: 0000000000000000 0 FILE LOCAL DEFAULT ABS test.c
40: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
41: 00000000004006c4 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
42: 0000000000000000 0 FILE LOCAL DEFAULT ABS
43: 0000000000600e18 0 NOTYPE LOCAL DEFAULT 18 __init_array_end
44: 0000000000600e20 0 OBJECT LOCAL DEFAULT 20 _DYNAMIC
45: 0000000000600e10 0 NOTYPE LOCAL DEFAULT 18 __init_array_start
46: 000000000040058c 0 NOTYPE LOCAL DEFAULT 16 __GNU_EH_FRAME_HDR
47: 0000000000601000 0 OBJECT LOCAL DEFAULT 22 _GLOBAL_OFFSET_TABLE_
48: 0000000000400570 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
49: 0000000000601020 0 NOTYPE WEAK DEFAULT 23 data_start
50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
51: 0000000000601030 0 NOTYPE GLOBAL DEFAULT 23 _edata
52: 0000000000400574 0 FUNC GLOBAL DEFAULT 14 _fini
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
54: 0000000000601020 0 NOTYPE GLOBAL DEFAULT 23 __data_start
55: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
56: 0000000000601028 0 OBJECT GLOBAL HIDDEN 23 __dso_handle
57: 0000000000400580 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
58: 0000000000400500 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
59: 0000000000601038 0 NOTYPE GLOBAL DEFAULT 24 _end
60: 0000000000400430 2 FUNC GLOBAL HIDDEN 13 _dl_relocate_static_pie
61: 0000000000400400 43 FUNC GLOBAL DEFAULT 13 _start
62: 0000000000601030 0 NOTYPE GLOBAL DEFAULT 24 __bss_start
63: 00000000004004e7 23 FUNC GLOBAL DEFAULT 13 main
64: 0000000000601030 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
65: 00000000004003c8 0 FUNC GLOBAL DEFAULT 11 _init
拣我们已知的. main, puts
能看懂名字的. _start, _init, __bss_start, _end, _edata, test.c, crtstuff.c,
其它就先不管了.
4 符号的构成要素
用readelf, 我们看到符号不仅有value, 而且还有size,
不仅有type, 而且还有Bind, Visibility, 还有index.
名称呢? 有的符号没有名称.
下面就要从计算机的角度来解释一下什么叫符号了.
不是含糊的概念, 而是准确的概念.
以64位cpu 为例.
- 前面说了, name 占4个bytes
- value 是一个8bytes 地址值
- size 是一个8bytes, 的大小值, 例如 main 函数,占用23bytes 大小
- index 是什么意思? 是说该符号属于哪个section, index 是section的index,用word表示,2bytes
- 把type 和 binding 合成一个byte, type 占低4bits, bind 占高4bits
5 符号在计算机中是如下定义的.
typedef struct
{
Elf64_Word st_name; /* Symbol name (string tbl index) */ 4字节
unsigned char st_info; /* Symbol type and binding */ 1字节
unsigned char st_other; /* Symbol visibility */ 1字节
Elf64_Section st_shndx; /* Section index */ 2字节
Elf64_Addr st_value; /* Symbol value */ 8字节
Elf64_Xword st_size; /* Symbol size */ 8字节
} Elf64_Sym;
对大小还不敢确认, 看看其elf64.h 中的定义
typedef uint32_t Elf64_Word;
typedef uint16_t Elf64_Section;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Xword;
6 补充1: type 的 定义
switch(sym_type) {
case 0: return "NOTYPE";
case 1: return "OBJECT";
case 2: return "FUNC";
case 3: return "SECTION";
case 4: return "FILE";
case 6: return "TLS";
case 7: return "NUM";
case 10: return "LOOS";
case 12: return "HIOS";
default: return "UNKNOWN";
}
7 补充2: bind 的 定义
switch(sym_bind) {
case 0: return "LOCAL";
case 1: return "GLOBAL";
case 2: return "WEAK";
case 3: return "NUM";
case 10: return "UNIQUE";
case 12: return "HIOS";
case 13: return "LOPROC";
default: return "UNKNOWN";
8 补充3: visibility 的 定义, 空间留了一个byte, 256种可能,但实际只有4种
switch(sym_vis) {
case 0: return "DEFAULT"; //可见
case 1: return "INTERNAL";
case 2: return "HIDDEN";
case 3: return "PROTECTED";
default: return "UNKNOWN";
}
9 补充4: section header index, 留了2个bytes, 默认是索引号, 但也有几个特例.
switch(shdr_idx) {
case SHN_ABS: return "ABS";
case SHN_COMMON: return "COM";
case SHN_UNDEF: return "UND";
case SHN_XINDEX: return "COM";
default: return std::to_string(shdr_idx);
}
#define SHN_UNDEF 0 /* Undefined section /
#define SHN_ABS 0xfff1 / Associated symbol is absolute /
#define SHN_COMMON 0xfff2 / Associated symbol is common /
#define SHN_XINDEX 0xffff / Index is in extra table. */
10. 小结:
我们从形态,构造,功能方面用白话介绍了符号的概念, 希望对想了解编译,连接,调试的朋友有所帮助.
想了解重定位的概念, 请参考链接:
elf 格式 relocation 概念
参考代码: https://gitee.com/hejinjing/elf-parser.git
更多推荐

所有评论(0)