Add a simple ELF loader
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2022-12-23 13:09:21 +01:00
parent e5ae2b0435
commit f2cc797599
Signed by: apio
GPG Key ID: B8A7D06E42258954
8 changed files with 253 additions and 6 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ toolchain/
build/
initrd/boot/moon
env-local.sh
initrd/bin/**

View File

@ -14,6 +14,8 @@ set(CMAKE_CXX_COMPILER x86_64-luna-g++)
set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
set(CMAKE_ASM_NASM_LINK_EXECUTABLE "x86_64-luna-ld <FLAGS> <CMAKE_ASM_NASM_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
set(CMAKE_FIND_ROOT_PATH ${LUNA_ROOT}/toolchain/x86-64-luna)
set(ARCH $ENV{ARCH})
@ -26,3 +28,4 @@ message(STATUS "Configuring Luna for ${ARCH}")
add_subdirectory(luna)
add_subdirectory(kernel)
add_subdirectory(apps)

6
apps/CMakeLists.txt Normal file
View File

@ -0,0 +1,6 @@
function(luna_app SOURCE_FILE APP_NAME)
add_executable(${APP_NAME} ${SOURCE_FILE})
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${APP_NAME}" DESTINATION ${LUNA_ROOT}/initrd/bin)
endfunction()
luna_app(app.asm app)

18
apps/app.asm Normal file
View File

@ -0,0 +1,18 @@
section .text
global _start
_start:
mov eax, ecx
push rdx
mov eax, 1
mov edi, hello_world
mov esi, 14
int 42h
nop
section .rodata
hello_world:
db 'Hello, world!', 0xa, 0
section .bss
array:
resb 10

View File

@ -16,6 +16,7 @@ set(SOURCES
src/thread/Thread.cpp
src/thread/Scheduler.cpp
src/InitRD.cpp
src/ELF.cpp
)
if("${ARCH}" MATCHES "x86_64")

137
kernel/src/ELF.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "ELF.h"
#include "Log.h"
#include "arch/CPU.h"
#include "arch/MMU.h"
#include "memory/MemoryManager.h"
#include <luna/Alignment.h>
#include <luna/Alloc.h>
#include <luna/CString.h>
#include <luna/ScopeGuard.h>
static bool can_execute_segment(u32 flags)
{
return flags & 1;
}
static bool can_write_segment(u32 flags)
{
return flags & 2;
}
/*static bool can_write_and_execute_segment(u32 flags)
{
return can_write_segment(flags) && can_execute_segment(flags);
}*/
ELFSegment::ELFSegment(u64 base, usize size) : m_base(base), m_size(size)
{
}
namespace ELFLoader
{
// FIXME: Check that all calls to read_contents() read the proper amount of bytes.
Result<ELFData> load(const TarStream::Entry& elf_entry, const TarStream& stream)
{
LinkedList<ELFSegment> segments;
auto guard = make_scope_guard([&] { segments.consume([](ELFSegment* segment) { delete segment; }); });
Elf64_Ehdr elf_header;
usize nread = stream.read_contents(elf_entry, &elf_header, 0, sizeof elf_header);
if (nread < sizeof elf_header)
{
kdbgln("Error while loading ELF: ELF header does not fit in entry");
return err(ENOEXEC);
}
if (memcmp(elf_header.e_ident, ELFMAG, SELFMAG) != 0)
{
kdbgln("Error while loading ELF: ELF header has no valid magic");
return err(ENOEXEC);
}
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
{
kdbgln("Error while loading ELF: ELF object is not 64-bit");
return err(ENOEXEC);
}
if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB)
{
kdbgln("Error while loading ELF: ELF object is not 2's complement little-endian");
return err(ENOEXEC);
}
if (elf_header.e_type != ET_EXEC)
{
kdbgln("Error while loading ELF: ELF object is not an executable");
return err(ENOEXEC);
}
if (elf_header.e_machine != EM_MACH)
{
kdbgln("Error while loading ELF: ELF object's target architecture does not match the current one (%s)",
CPU::platform_string());
return err(ENOEXEC);
}
if (elf_header.e_phnum == 0)
{
kdbgln("Error while loading ELF: ELF object has no program headers");
return err(ENOEXEC);
}
kdbgln("ELF: Loading ELF with entry=%#.16lx", elf_header.e_entry);
usize i;
Elf64_Phdr program_header;
for (stream.read_contents(elf_entry, &program_header, elf_header.e_phoff, sizeof program_header), i = 0;
i < elf_header.e_phnum;
i++, stream.read_contents(elf_entry, &program_header, elf_header.e_phoff + (i * elf_header.e_phentsize),
sizeof program_header))
{
if (program_header.p_type == PT_LOAD)
{
kdbgln("ELF: Loading segment (offset=%zu, base=%#.16lx, filesize=%zu, memsize=%zu)",
program_header.p_offset, program_header.p_vaddr, program_header.p_filesz,
program_header.p_memsz);
check(is_aligned<ARCH_PAGE_SIZE>(program_header.p_vaddr));
/*expect(!can_write_and_execute_segment(program_header.p_flags),
"Segment is both writable and executable");*/
ELFSegment* segment = TRY(make<ELFSegment>(program_header.p_vaddr, program_header.p_memsz));
segments.append(segment);
int flags = MMU::User | MMU::NoExecute;
if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite;
else if (can_execute_segment(program_header.p_flags))
flags &= ~MMU::NoExecute;
// Allocate physical memory for the segment
TRY(MemoryManager::alloc_at(program_header.p_vaddr,
get_blocks_from_size(program_header.p_memsz, ARCH_PAGE_SIZE), flags));
// Load the file section of the segment
stream.read_contents(elf_entry, (void*)program_header.p_vaddr, program_header.p_offset,
program_header.p_filesz);
// Fill out the rest of the segment with 0s
memset((void*)(program_header.p_vaddr + program_header.p_filesz), 0,
program_header.p_memsz - program_header.p_filesz);
}
else { kdbgln("ELF: Encountered non-loadable program header, skipping"); }
}
if (segments.count() == 0)
{
kdbgln("Error while loading ELF: No loadable segments");
return err(ENOEXEC);
}
guard.deactivate();
return ELFData { segments, elf_header.e_entry };
}
}

78
kernel/src/ELF.h Normal file
View File

@ -0,0 +1,78 @@
#pragma once
#include <luna/LinkedList.h>
#include <luna/TarStream.h>
#include <luna/Types.h>
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASS64 2 /* 64-bit objects */
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ET_EXEC 2 /* Executable file */
#define PT_LOAD 1 /* Loadable program segment */
#ifdef ARCH_X86_64
#define EM_MACH 62 /* AMD x86-64 architecture */
#else
#error "Unknown architecture."
#endif
typedef struct
{
u8 e_ident[16]; /* Magic number and other info */
u16 e_type; /* Object file type */
u16 e_machine; /* Architecture */
u32 e_version; /* Object file version */
u64 e_entry; /* Entry point virtual address */
u64 e_phoff; /* Program header table file offset */
u64 e_shoff; /* Section header table file offset */
u32 e_flags; /* Processor-specific flags */
u16 e_ehsize; /* ELF header size in bytes */
u16 e_phentsize; /* Program header table entry size */
u16 e_phnum; /* Program header table entry count */
u16 e_shentsize; /* Section header table entry size */
u16 e_shnum; /* Section header table entry count */
u16 e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
typedef struct
{
u32 p_type; /* Segment type */
u32 p_flags; /* Segment flags */
u64 p_offset; /* Segment file offset */
u64 p_vaddr; /* Segment virtual address */
u64 p_paddr; /* Segment physical address */
u64 p_filesz; /* Segment size in file */
u64 p_memsz; /* Segment size in memory */
u64 p_align; /* Segment alignment */
} Elf64_Phdr;
struct ELFSegment : public LinkedListNode<ELFSegment>
{
u64 base() const
{
return m_base;
}
usize size() const
{
return m_size;
}
ELFSegment(u64 base, usize size);
private:
u64 m_base;
usize m_size;
};
struct ELFData
{
LinkedList<ELFSegment> segments;
u64 entry;
};
namespace ELFLoader
{
Result<ELFData> load(const TarStream::Entry& elf_entry, const TarStream& stream);
};

View File

@ -1,3 +1,4 @@
#include "ELF.h"
#include "InitRD.h"
#include "Log.h"
#include "arch/CPU.h"
@ -67,6 +68,8 @@ Result<void> init()
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
MMU::unmap(0x400000);
TarStream::Entry entry;
while (TRY(g_initrd.read_next_entry().try_set_value_with_specific_error(entry, 0)))
{
@ -75,11 +78,11 @@ Result<void> init()
kinfoln("Found file %s in initial ramdisk, of size %s", entry.name,
to_dynamic_unit(entry.size).release_value().chars());
if (!strcmp(entry.name, "sys/config"))
if (!strcmp(entry.name, "bin/app"))
{
auto contents = TRY(g_initrd.read_contents_as_string(entry, 0, entry.size));
kinfoln("%s", contents.chars());
auto data = TRY(ELFLoader::load(entry, g_initrd));
data.segments.consume([](ELFSegment* segment) { delete segment; });
kinfoln("Loaded ELF with entry=%#.16lx", data.entry);
}
}
}