Add a simple ELF loader
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
e5ae2b0435
commit
f2cc797599
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ toolchain/
|
||||
.vscode/
|
||||
build/
|
||||
initrd/boot/moon
|
||||
env-local.sh
|
||||
env-local.sh
|
||||
initrd/bin/**
|
@ -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})
|
||||
@ -25,4 +27,5 @@ endif()
|
||||
message(STATUS "Configuring Luna for ${ARCH}")
|
||||
|
||||
add_subdirectory(luna)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(apps)
|
6
apps/CMakeLists.txt
Normal file
6
apps/CMakeLists.txt
Normal 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
18
apps/app.asm
Normal 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
|
@ -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
137
kernel/src/ELF.cpp
Normal 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
78
kernel/src/ELF.h
Normal 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);
|
||||
};
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user