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
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ toolchain/
|
|||||||
build/
|
build/
|
||||||
initrd/boot/moon
|
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_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(CMAKE_FIND_ROOT_PATH ${LUNA_ROOT}/toolchain/x86-64-luna)
|
||||||
|
|
||||||
set(ARCH $ENV{ARCH})
|
set(ARCH $ENV{ARCH})
|
||||||
@ -26,3 +28,4 @@ message(STATUS "Configuring Luna for ${ARCH}")
|
|||||||
|
|
||||||
add_subdirectory(luna)
|
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/Thread.cpp
|
||||||
src/thread/Scheduler.cpp
|
src/thread/Scheduler.cpp
|
||||||
src/InitRD.cpp
|
src/InitRD.cpp
|
||||||
|
src/ELF.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if("${ARCH}" MATCHES "x86_64")
|
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 "InitRD.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "arch/CPU.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("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
|
||||||
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
|
||||||
|
|
||||||
|
MMU::unmap(0x400000);
|
||||||
|
|
||||||
TarStream::Entry entry;
|
TarStream::Entry entry;
|
||||||
while (TRY(g_initrd.read_next_entry().try_set_value_with_specific_error(entry, 0)))
|
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,
|
kinfoln("Found file %s in initial ramdisk, of size %s", entry.name,
|
||||||
to_dynamic_unit(entry.size).release_value().chars());
|
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));
|
auto data = TRY(ELFLoader::load(entry, g_initrd));
|
||||||
|
data.segments.consume([](ELFSegment* segment) { delete segment; });
|
||||||
kinfoln("%s", contents.chars());
|
kinfoln("Loaded ELF with entry=%#.16lx", data.entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user