I'm trying to make a simple operating system kernel higher half. When using Grub as a bootloader as I am, there must also be some lower half (32 bit) code. Because I want to keep this 32 bit code as brief as possible, I do not want to write an ELF loader in it just to load the 64 bit code because that would be patently absurd (this is in fact the most common solution, but I would like to avoid it if possible).
I discovered that linker scripts allow load addresses that differ from virtual addresses. This is useful so that I can load the 64 bit sections to fit in a small binary and then use virtual memory to map the proper virtual addresses to the physical addresses they were loaded at. This works except that the low text section is not put in the text segment. The entry point, _start
, is in this section.
I can't put the low text section (where _start
resides) in the text segment unless I specify the text segment in a PHDRS
command. Of course, using this command makes the linker decide to take a pass on generating the normally expected segments. When I do this too, the sections end up overlapping and I am not entirely sure why. I specify the segments in the order data, rodata, text, and the sections are the same, and yet their load memory addresses are assigned with rodata and data swapped and all three overlapping.
Here is my linker script:
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
}
I do not think the code is relevant to this error. When I link my object files using this linker script (additionally with -n --gc-sections
), I get this error:
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]
The load memory addresses are in the order rodata, data, text, even though I expect they should be in the order data, rodata, text since the sections are specified in that order with AT
specifiers with monotonically non decreasing positions (assuming the sections do not have negative size).
I should specify that I am using "segment" to mean one of the entries in the ELF program header (PHDRS
in the linker script) and "section" to mean one of the entries in the ELF section header (SECTIONS
in the linker script). I believe this to be correct terminology but acknowledge that I have an understanding of linker files and the ELF format that is limited at best. For whatever reason, Grub will not load an ELF file if its entry point is not in a segment.
Why indeed are the sections not in the order I expect them to be in, and how can I make them be? Thank you.