本帖最后由 我爱下载 于 2023-4-23 15:00 编辑
# 第5章和第6章读后感(vmlinux.ld.S分析)
*以下的分析为个人理解,可能不正确,分析不到位,请批评指正。*
内核生成内核文件的时候,其实这个过程分两步,一个是“编译”,另一个是“链接”的过程,vmlinux.lds.S要做的就是告诉编译器如何链接编译好的各个内核.o文件。
1) 通过include命令,包含所需要的其它链接文件或头文件。
```
#include
#define LOAD_OFFSET KERNEL_LINK_ADDR
#include
#include
#include
#include
#include
#include "image-vars.h"
#include
```
2) 接下来的命令:
```
OUTPUT_ARCH(riscv)
ENTRY(_start)
```
分别表明了链接输出文件基于内核架构为RISC-V,和程序入口点标号为_start;
```
. = LOAD_OFFSET;
_start = . ;
```
上面的两条命令定义了当前的链接地址为LOAD_OFFSET,并且定义了标号_start的地址为当前地址。
我感觉似乎是告诉系统,启动的时候直接从LOAD_OFFSET地址加载,不知道是否正确。
3) 代码段的定义:
```
. = ALIGN(PAGE_SIZE);
.text : {
_text = .;
_stext = .;
TEXT_TEXT
SCHED_TEXT
CPUIDLE_TEXT
LOCK_TEXT
KPROBES_TEXT
ENTRY_TEXT
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
*(.fixup)
_etext = .;
}
```
首先定义.text代码段按照PAGE_SIZE大小对齐。并且定义了代码段的开始符号_stext和结束符号_etext,同时将所有需要连接的文件中满足如下段的代码放入.text段中,输入段包括:
TEXT_TEXT
SCHED_TEXT
CPUIDLE_TEXT
LOCK_TEXT
KPROBES_TEXT
ENTRY_TEXT
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
*(.fixup)
4) 定义.init.text段:
```
. = ALIGN(SECTION_ALIGN);
__init_begin = .;
__init_text_begin = .;
.init.text : AT(ADDR(.init.text) - LOAD_OFFSET) ALIGN(SECTION_ALIGN) { \
_sinittext = .; \
INIT_TEXT \
_einittext = .; \
}
```
首先定义.init.text初始化代码段按照SECTION_ALIGN大小对齐。并且定义了初始化代码段的开始符号_sinittext和结束符号_einittext,定义初始化代码段.init.text的加载地址为(ADDR(.init_text) – LOAD_OFFSET)并且在SECTION_ALIGN对齐的下一个地址,同时将所有需要连接的文件中满足INIT_TEXT段的代码放入.init.text段。
我的感觉,__init_begin的地址和_sinittext的地址是不同的,__init_begin的地址应该是紧接着.text段的后面,而_sinittext的地址受限于AT的命令和ALIGN(SECTION_ALIGN)的要求。
```
. = ALIGN(8);
__soc_early_init_table : {
__soc_early_init_table_start = .;
KEEP(*(__soc_early_init_table))
__soc_early_init_table_end = .;
}
__soc_builtin_dtb_table : {
__soc_builtin_dtb_table_start = .;
KEEP(*(__soc_builtin_dtb_table))
__soc_builtin_dtb_table_end = .;
}
/* we have to discard exit text and such at runtime, not link time */
.exit.text :
{
EXIT_TEXT
}
__init_text_end = .;
```
紧接着的内容按照8字节对齐的下一个地址开始,存放了__soc_early_init_table和__soc_builtin_dtb_table内容,定义了.exit.text段,存放EXIT_TEXT内容。
5) init.data有关的数据段:
```
. = ALIGN(SECTION_ALIGN);
/* Start of init data section */
__init_data_begin = .;
INIT_DATA_SECTION(16)
.exit.data :
{
EXIT_DATA
}
PERCPU_SECTION(L1_CACHE_BYTES)
.rel.dyn : {
*(.rel.dyn*)
}
__init_data_end = .;
. = ALIGN(8);
.alternative : {
__alt_start = .;
*(.alternative)
__alt_end = .;
}
__init_end = .;
```
按照SECTION_ALIGN字节对齐的下一个地址开始,把所有EXIT_DATA存入.exit.data段中,将所有连接文件中.rel.dyn*段的数据统一放到.rel.dyn段中。
接下来,按照8字节对齐的下一个地址开始,将所有连接文件中.alternative段中的数据统一放到.alternative段中。
6) 数据段的定义:
```
/* Start of data section */
_sdata = .;
RO_DATA(SECTION_ALIGN)
.srodata : {
*(.srodata*)
}
. = ALIGN(SECTION_ALIGN);
_data = .;
RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN)
.sdata : {
__global_pointer$ = . + 0x800;
*(.sdata*)
}
#ifdef CONFIG_EFI
.pecoff_edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGNMENT); }
__pecoff_data_raw_size = ABSOLUTE(. - __pecoff_text_end);
#endif
/* End of data section */
_edata = .;
```
首先定义了只读数据区,将所有链接文件中.srodata*段中的数据,统一存放到.srodata段中。然后按照SECTION_ALIGN对齐的下一个地址存储所有链接文件中.sdata*段的数据到.sdata段中。