hac*_*atu 5 c assembly linker elf linker-scripts
我正在尝试制作一个简单的操作系统内核的上半部分。当像我一样使用 Grub 作为引导加载程序时,还必须有一些下半部分(32 位)代码。因为我想让这个 32 位代码尽可能简短,所以我不想在其中编写一个 ELF 加载程序只是为了加载 64 位代码,因为这显然是荒谬的(这实际上是最常见的解决方案,但我如果可能的话想避免它)。
我发现链接描述文件允许加载与虚拟地址不同的地址。这很有用,这样我就可以加载 64 位部分以适应小型二进制文件,然后使用虚拟内存将正确的虚拟地址映射到它们加载的物理地址。除了低文本部分未放入文本段之外,此方法有效。入口点_start位于本节中。
除非我在命令中指定文本段,否则我无法将低文本部分(所在_start位置)放入文本段中PHDRS。当然,使用此命令使链接器决定放弃生成通常预期的段。当我也这样做时,这些部分最终会重叠,我不完全确定为什么。我按照数据、rodata、文本的顺序指定段,并且这些部分是相同的,但它们的加载内存地址是用rodata和交换的数据分配的,并且所有三个都是重叠的。
这是我的链接器脚本:
ENTRY(_start)
PHDRS {
.low PT_LOAD FILEHDR PHDRS;
.data PT_LOAD;
.rodata PT_LOAD;
.text PT_LOAD;
}
SECTIONS {
. = 1M;
.data_low BLOCK(4K) : ALIGN(4K) {
*(.bss_low)
} : .low
.rodata_low BLOCK(4K) : ALIGN(4K) {
KEEP(*(.multiboot_low))
*(.rodata_low)
} : .low
.text_low BLOCK(4K) : ALIGN(4K) {
*(.text_low)
} : .low
.stack 0xC0000000 : AT(0x200000) ALIGN(4K) {
*(.bootstrap_stack)
} : .data
_LADD_ = LOADADDR(.stack) + SIZEOF(.stack);
.data BLOCK(4K) : AT(_LADD_) ALIGN(4K) {
*(COMMON)
*(.bss)
} : .data
_LADD_ += SIZEOF(.data);
.rodata BLOCK(4K) : AT(_LADD_) ALIGN(4K) {
*(.rodata)
} : .rodata
_LADD_ += SIZEOF(.rodata);
.text BLOCK(4K) : AT(_LADD_) ALIGN(4K) {
*(.text)
} : .text
}
Run Code Online (Sandbox Code Playgroud)
我认为该代码与此错误无关。当我使用此链接器脚本(另外还使用 )链接我的目标文件时-n --gc-sections,出现以下错误:
ld: section .data loaded at [0000000000200020,000000000020103f] overlaps section .rodata loaded at [0000000000200010,00000000002000d0]
ld: section .text loaded at [00000000002000d1,00000000002017ce] overlaps section .data loaded at [0000000000200020,000000000020103f]
Run Code Online (Sandbox Code Playgroud)
加载内存地址的顺序是rodata、data、text,尽管我希望它们应该是data、rodata、text的顺序,因为这些节是按该顺序指定的,并且带有AT单调非递减位置的说明符(假设这些节不具有负尺寸)。
我应该指定我使用“段”来表示 ELF 程序头中的条目之一(PHDRS在链接描述文件中),使用“节”来表示 ELF 节头中的条目之一(SECTIONS在链接器脚本中)。我相信这是正确的术语,但承认我对链接器文件和 ELF 格式的理解最多是有限的。无论出于何种原因,如果 ELF 文件的入口点不在段中,Grub 都不会加载该文件。
为什么这些部分确实不符合我期望的顺序,我该如何调整它们?谢谢。