137 lines
4.8 KiB
C++
137 lines
4.8 KiB
C++
|
#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 };
|
||
|
}
|
||
|
}
|