Appearance
.comment / .note.* / .shstrtab / .gnu_debuglink / .gnu.build-id —— 元数据与调试辅助节
前面四篇已经把核心功能节讲完了,还有一类"不用来跑代码、但用来辅助分析"的元数据节。它们虽然不影响程序运行,但在调试、溯源、符号分离、ABI 兼容性检查等场景下不可或缺:
.comment告诉你用什么编译器编的,.note.*记录 ABI 标签和 Build ID,.shstrtab是 Section 名字的仓库,.gnu_debuglink让 gdb 能自动找到被 strip 分离走的调试符号。本篇把这些"元数据节"一次性收尾。
更新时间:2026-08-06
一、六节全景

二、.comment —— 编译器版本
readelf -p .comment 直接读出编译器和版本号字符串:
bash
$ readelf -p .comment ./cpu_demo
String dump of section '.comment':
[ 0] GCC: (GNU) 8.5.0 20210514 (Red Hat 8.5.0-4)
[ 2c] GCC: (GNU) 8.5.0 20210514 (Red Hat 8.5.0-4)- 每个
.o的.comment被链接器合并,所以同一个版本可能出现多次 Address=0x0,不加载进内存,strip 后会删除- 纯信息记录,不影响运行
三、.note.* —— Note Section(三个最重要的)
ELF Note 是一种通用的"键值记录"格式。每个 note 包含 {namesz, descsz, type, name, desc}。.note.* 按用途分几个子节。
3.1 .note.ABI-tag —— ABI 兼容性标记
bash
$ readelf -n ./cpu_demo
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0| 字段 | 含义 |
|---|---|
OS: Linux | 目标操作系统 |
ABI: 3.2.0 | 内核 ABI 版本(对应运行所需的最低内核版本) |
动态链接器 ld.so 在加载时会检查这个 tag,确保 .so 不会在太老的内核上运行。
3.2 .note.gnu.build-id —— 二进制唯一标识
bash
$ readelf -n ./cpu_demo | grep -A2 'Build ID'
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 7a8b3c9d1e2f4a5b6c7d8e9f0a1b2c3d4e5f6a7bBuild ID 是一个 160-bit SHA1 哈希(或 128-bit MD5):
- 链接时计算(对所有代码/数据的摘要),保证每次构建不同
- 调试符号和 core dump 都用它做匹配——gdb 加载 core 时先读 build-id,再在
$HOME/.debug/里找同 build-id 的.debug文件 - 比文件名匹配可靠得多——你重命名了二进制,build-id 不变,gdb 仍然能找到正确的符号
-Wl,--build-id(默认打开)生成;-Wl,--build-id=none 关闭。
3.3 .note.gnu.property —— 安全属性
记录与安全相关的 CPU 特性要求(如 CET / IBT 支持、x86 ISA level 等)。动态链接器用它做兼容性检查——二进制声称需要某个 CPU 特性而运行时环境不支持,直接拒绝加载。
四、.shstrtab —— Section 名字的字符串池
.shstrtab 是所有 Section 名字的字符串表。ELF Header 的 e_shstrndx 字段指的就是它在 Section Header Table 中的索引号。
bash
$ readelf -S ./cpu_demo | grep shstrtab
[36] .shstrtab STRTAB 0000000000000000 00032b04
0000016a S 0 0 1工作原理和 .strtab 完全相同(字节池 + 偏移索引),只是它存的是**"Section 名字"**(.text, .data, ...),而不是符号名。
sh_name字段存的是到.shstrtab的偏移- readelf 读到
sh_name=0x0a,就从.shstrtab[0x0a]起读到\0,拿到".text" Address=0x0,不加载进内存
一句话记忆:.strtab = 符号名字符串池(symbol names),.shstrtab = Section 名字字符串池(section header names)。.dynstr = 动态符号名字符串池(dynamic symbol names)。
五、.gnu_debuglink —— 分离的调试符号指针
当用 objcopy --add-gnu-debuglink=app.debug app 处理后,会在被 strip 的可执行文件末尾追加一个 .gnu_debuglink 节。它只含两种信息:
| 内容 | 含义 |
|---|---|
外部 .debug 文件的文件名 | 如 cpu_demo.debug(无路径,gdb 去预定义路径搜) |
| 4 字节 CRC32 校验和 | 确保 .debug 文件和二进制匹配 |
bash
$ objdump -s -j .gnu_debuglink ./cpu_demo
Contents of section .gnu_debuglink:
0000 6370755f 64656d6f 2e646562 75670000 cpu_demo.debug..
0010 7b2a9d1f {*..
# ^^^^^^^^ ← CRC32gdb 加载被 strip 的二进制时:
- 读
.gnu_debuglink→ 拿到文件名cpu_demo.debug - 去
/usr/lib/debug/、/usr/local/lib/debug/、二进制同目录下搜这个文件 - 比对 CRC32 → 匹配则自动加载符号
六、.gnu.build.attributes —— 全局构建属性
GCC 10+ 支持,记录全局构建属性(如 -fcf-protection 开启情况、-march= 级别等),供静态分析和安全审计使用。目前还是较新的特性,日常很少直接操作。
七、字符串表三兄弟对照
EL文件里有三个字符串表,职责分得很清:
| 节名 | 存什么 | 谁索引它 | 示例 |
|---|---|---|---|
.strtab | 符号名 | .symtab 的 st_name | "main", "printf" |
.dynstr | 动态符号名 | .dynsym 的 st_name | "printf@GLIBC_2.2.5" |
.shstrtab | Section 名 | 各 section header 的 sh_name | ".text", ".data" |
三者结构完全一样(字节池 + \0 分割),仅存储对象不同。
八、实操速查
bash
# 看编译器版本
readelf -p .comment ./app
# 看所有 Note(ABI tag + Build ID + 属性)
readelf -n ./app
# 看 Build ID(简洁输出)
readelf -n ./app | grep 'Build ID'
# 看 .shstrtab 内容
readelf -p .shstrtab ./app | head
# 看 .gnu_debuglink 指向的外部 debug 文件
objdump -s -j .gnu_debuglink ./app
# 验证 debug 文件 CRC32 匹配
readelf -x .gnu_debuglink ./app
# 最后 4 字节对比:crc32 ./app.debug
# 看所有 meta section 大小
readelf -S ./app | grep -E 'comment|note|shstrtab|debuglink|build'九、与相关文档的关系
| 文档 | 覆盖内容 | 本文的关系 |
|---|---|---|
| elf-format.md §3.2 | .shstrtab 和 e_shstrndx | 本文详细展开字符串表三兄弟 |
| symbol-table.md | .strtab / .dynstr | 本文对比三者结构并给出对照表 |
| symbol-separation.md | --add-gnu-debuglink 操作 | 本文解释 .gnu_debuglink 的存储格式 |
| compile-link-load.md | 链接与调试 | Build ID 作为版本追溯链路 |
| text-data-bss.md | .text/.data/.bss | 元数据节与功能节的对照 |
一句话总结:
.comment记录编译器版本(纯注释),.note.*记录 ABI 标签和 Build ID(加载时检查 + 符号匹配),.shstrtab是 Section 名字的字符串池(和.strtab/.dynstr并列的第三字符串表),.gnu_debuglink指向剥离的调试符号文件(含文件名 + CRC32 校验)。它们都不参与程序运行逻辑,但决定了你能多好地分析、调试、追溯这个程序。