Linux vmlinux中的二进制布局

Linux内核的vmlinux是未经压缩的ELF格式可执行文件,其二进制布局由链接脚本控制,主要分为以下几个关键部分:


1. ELF文件结构

  • ELF头部:描述文件的基本信息(如架构、入口点、程序头和节区头的位置)。
  • 程序头表(Program Headers):定义如何将段(如代码、数据)加载到内存。
  • 节区(Sections):实际代码、数据和其他信息的存储区域。
  • 节区头表(Section Headers):描述各节区的元数据(位于文件末尾)。

2. 核心节区布局

代码段

  • **.text**:内核核心代码(如调度、内存管理)。
  • **.text.head**(某些架构):启动相关代码(如x86的实模式初始化)。
  • **.init.text**:初始化阶段代码,完成后可能被释放。

只读数据

  • **.rodata**:只读数据(如字符串常量、驱动名称)。

数据段

  • **.data**:已初始化的全局变量。
  • **.data..page_aligned**:页面对齐的全局数据(如页表)。

未初始化数据

  • **.bss**:未初始化的全局变量,文件不占空间,加载时清零。

特殊用途节区

  • **__ex_table**:异常处理表(如x86的故障恢复指令)。
  • __ksymtab / **__ksymtab_gpl**:内核符号表(用于模块依赖)。
  • **.init.data**:初始化阶段数据,完成后释放。
  • **.rodata.str1.1**:优化后的只读字符串池。
  • **.notes**:内核版本和构建信息。

3. 内存布局与加载

  • 入口点:由ELF头部指定,通常指向架构相关的启动代码(如x86的startup_64)。
  • 虚拟地址:内核代码和数据链接到高地址(如x86_64的0xffffffff80000000),与物理地址通过页表映射。
  • 程序头加载:通过程序头表指导加载器将各段映射到内存(如.text到可执行区域,.data到读写区域)。

4. 链接脚本控制

链接脚本(如arch/x86/kernel/vmlinux.lds.S)决定节区顺序和对齐,典型逻辑如下:

  1. 放置启动代码(如.text.head)。
  2. 主代码段(.text)和只读数据(.rodata)。
  3. 数据段(.data)和BSS(.bss)。
  4. 初始化代码和数据(.init.*),可能标记为可释放。
  5. 特殊节区(如符号表、异常表)。

5. 工具验证

  • **readelf -S vmlinux**:查看节区列表及文件偏移。
  • **objdump -d vmlinux**:反汇编代码段。
  • **nm vmlinux**:查看符号地址。

示例布局(x86_64简化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ELF Header
Program Headers
LOAD (RX) : .text, .rodata
LOAD (RW) : .data, .bss
Sections:
.text.head
.text
.rodata
.data..page_aligned
.data
.bss
.init.text
__ex_table
__ksymtab
.debug_info (可选)
Section Headers

关键点

  • 调试符号:若开启CONFIG_DEBUG_INFO,会包含.debug_*节区,显著增大文件。
  • 架构差异:ARM可能包含.vectors(异常向量表),x86关注实模式代码。
  • 优化:某些节区可能合并(如.text.init.text)或按需对齐。

通过链接脚本和ELF工具,可以精确控制内核的二进制布局,确保正确加载和执行。