1.前言
在 ELF(Executable and Linkable Format)文件中,常见的段(Sections)包括:
.text:包含可执行代码(指令)。
.rodata:包含只读数据,如字符串字面量和常量。
.data:包含已初始化的全局和静态变量。
.bss:包含未初始化的全局和静态变量。这个段在文件中不占空间,但在内存中分配。
.symtab:包含符号表,其中包括函数和变量的信息。
.strtab:包含字符串表,存储符号的名称。
.rel.text:包含 .text 段的重定位信息。
.rel.data:包含 .data 段的重定位信息。
.debug:包含调试信息。
.comment:包含版本控制信息。
.plt:过程链接表(Procedure Linkage Table),用于动态链接。
.got:全局偏移表(Global Offset Table),用于动态链接。
.init:包含初始化代码段。
.fini:包含终结代码段。
这些段帮助组织 ELF 文件中的不同类型的数据和代码,使链接器和加载器更容易处理文件。
2.链接原始文件
OUTPUT_ARCH(riscv)
ENTRY(_start)
BASE_ADDRESS = 0x80200000;
SECTIONS
{
. = BASE_ADDRESS;
skernel = .;
stext = .;
.text : {
*(.text.entry)
*(.text .text.*)
}
. = ALIGN(4K);
etext = .;
srodata = .;
.rodata : {
*(.rodata .rodata.*)
*(.srodata .srodata.*)
}
. = ALIGN(4K);
erodata = .;
sdata = .;
.data : {
*(.data .data.*)
*(.sdata .sdata.*)
}
. = ALIGN(4K);
edata = .;
.bss : {
*(.bss.stack)
sbss = .;
*(.bss .bss.*)
*(.sbss .sbss.*)
}
. = ALIGN(4K);
ebss = .;
ekernel = .;
/DISCARD/ : {
*(.eh_frame)
}
}
3.链接文件内容解析
ld
文件是 GNU 链接器(GNU linker)的脚本文件,用于定义如何将目标文件链接成最终的可执行文件。它指定了输出文件的结构、各个段的地址和排列方式等。以下是 ld
文件的一些关键部分和它们的作用:
OUTPUT_ARCH:指定输出文件的架构类型。
ENTRY:指定程序的入口点。
BASE_ADDRESS:定义一个基地址,用于设置段的起始地址。
SECTIONS:定义各个段的布局和内容。
以下是章节2中的脚本文件内容解析:
OUTPUT_ARCH(riscv) // 指定输出文件的架构为 RISC-V
ENTRY(_start) // 指定程序的入口点为 _start
BASE_ADDRESS = 0x80200000; // 定义基地址为 0x80200000
SECTIONS
{
. = BASE_ADDRESS; // 设置当前地址为基地址
skernel = .; // 定义符号 skernel,表示内核的起始地址
stext = .; // 定义符号 stext,表示文本段的起始地址
.text : { // 定义 .text 段
*(.text.entry) // 包含所有 .text.entry 段
*(.text .text.*) // 包含所有 .text 和 .text.* 段
}
. = ALIGN(4K); // 将当前地址对齐到 4K 边界
etext = .; // 定义符号 etext,表示文本段的结束地址
srodata = .; // 定义符号 srodata,表示只读数据段的起始地址
.rodata : { // 定义 .rodata 段
*(.rodata .rodata.*) // 包含所有 .rodata 和 .rodata.* 段
*(.srodata .srodata.*) // 包含所有 .srodata 和 .srodata.* 段
}
. = ALIGN(4K); // 将当前地址再次对齐到 4K 边界
这个 linker.ld 文件定义了一个 RISC-V 架构的可执行文件的链接布局。它指定了程序的入口点 _start,并定义了 .text 和 .rodata 段的布局和对齐方式。
4.引用
rCore学习章节:https://learningos.cn/rCore-Tutorial-Guide-2024S/chapter1/4mini-rt-baremetal.html