Up until now, I am following the guide for creating a 64-bit kernel using a separate loader. However, in a rather important section in this guide the author used code which is not really helpful: Section Loader, code:
Code: Select all
#include "elf64.h" // Also requires elf64.c
char* kernel_elf_space[sizeof(elf_file_data_t)];
elf_file_data_t* kernel_elf = (elf_file_data_t*) kernel_elf_space; /* Pointer to elf file structure (remember there is no memory management yet) */
/* This function parses the ELF file and returns the entry point */
void* load_elf_module(multiboot_uint32_t mod_start, multiboot_uint32_t mod_end){
unsigned long err = parse_elf_executable((void*)mod_start, sizeof(elf_file_data_t), kernel_elf); /* Parses ELF file and returns an error code */
if(err == 0){ /* No errors occurred while parsing the file */
for(int i = 0; i < kernel_elf->numSegments; i++){
elf_file_segment_t seg = kernel_elf->segments[i]; /* Load all the program segments into memory */
/* if you want to do relocation you should do so here, */
const void* src = (const void*) (mod_start + seg.foffset); /* though that would require some changes to parse_elf_executable */
memcpy((void*) seg.address, src, seg.flength);
}
return (void*) kernel_elf->entryAddr; /* Finally we can return the entry address */
}
return NULL;
}
Currently the situation is as follows:
I am using the linker script for the kernel, which is similar to linux in structure:
Code: Select all
ENTRY(_entry)
KERNEL_VMA = 0xffffffff80100000;
KERNEL_OFF = 0xffffffff80000000;
SECTIONS
{
. = KERNEL_VMA;
_kernel = .;
.text ALIGN(4K) : AT(ADDR(.text) - KERNEL_OFF)
{
_text = .;
*(.text)
_etext = .;
}
...
The values are as follows:
Code: Select all
memmap: 0x0 -> 0x0, flags: 0x3, size: 0x400000
memmap: 0xffff880000000000 -> 0x0, flags: 0x3, size: 0x40000000
memmap: 0xffffffff80000000 -> 0x0, flags: 0x3, size: 0x20000000
Kernel module located at 0x193000 - 0xAA8AF0
Segment [0]: 0xffffffff80000000 -> 0x193000, size: 0x9130b0
Section [1]: 0xffffffff80100000 -> 0x293000: .text, size: 0x5711
Section [2]: 0xffffffff80106000 -> 0x299000: .rodata, size: 0x9ee
Section [3]: 0xffffffff801069f0 -> 0x2999f0: .eh_frame, size: 0x1990
Section [4]: 0xffffffff80109000 -> 0x29c000: .data, size: 0x88
Section [5]: 0xffffffff8010A000 -> 0x29d000: .bss, size: 0x8830
Section [6]: 0xffffffff80113000 -> 0x2a6000: .mm, size: 0x8000b0
Code: Select all
/* Debugging */
void dbg_print_segments(elf64_ehdr_t *elf64_ehdr)
{
elf64_phdr_t *elf64_phdr = get_elf64_phdr(elf64_ehdr);
for(uint32_t i = 0; i < elf64_ehdr->e_phnum; i++) {
elf64_phdr_t *elf64_segment = &elf64_phdr[i];
uint64_t vaddr = elf64_segment->p_vaddr;
uint64_t paddr = void_ptrtu32(elf64_ehdr) + elf64_segment->p_offset;
uint64_t memsz = elf64_segment->p_memsz;
info("Segment [%d]: 0x%016llx -> 0x%llx, Size: %llx",
i, vaddr, paddr, memsz);
}
}
void dbg_print_sections(elf64_ehdr_t *elf64_ehdr)
{
elf64_shdr_t *elf64_shdr = get_elf64_shdr(elf64_ehdr);
/* Index 0 is reserved */
for(uint32_t i = 1; i < elf64_ehdr->e_shnum; i++) {
elf64_shdr_t *elf64_section = &elf64_shdr[i];
char *sname = get_elf64_shdr_name(elf64_ehdr, elf64_section);
uint64_t vaddr = elf64_section->sh_addr;
uint64_t paddr = void_ptrtu32(elf64_ehdr) + elf64_section->sh_offset;
uint64_t memsz = elf64_section->sh_size;
if(sname != NULL) {
info("Section [%d]: 0x%016llx -> 0x%llx: %s, Size: %llx",
i, vaddr, paddr, sname, memsz);
}
}
}
So I have quite the dilemma, since I cannot simply change the entry point address by adding an offset, because every function is inside the text section starting at 0xffffffff80100000, just as my linker script above intended to. What do I do?
Should I maybe try to create a minimal example and post it on GitHub?
EDIT: Added minimal crash example on GitHub: click here!
In this case, I removed uneccessary code to reproduce the issue, when I compiled the code, the entry point for the kernel was located at 0xffffffff80100af8 according to the elf header, but the actual entry point is at 0xffffffff80293af8, so it is located 0x193000 bytes further away, just as described above.
I am mapping the addresses:
Identity mapping 0x0 (virtual) to 0x0 (physical) with 4MiB length
Mapping of complete memory 0xffff880000000000 (virtual) to 0x0 (physical) with 1GiB length
Kernel text mapping 0xffffffff80000000 (virtual) to 0x0 (physical) with 512MiB length, all according to the Linux documentation.