forked from Bananymous/banan-os
				
			
			update main #1
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -0,0 +1,290 @@
 | 
			
		|||
#include <BAN/ScopeGuard.h>
 | 
			
		||||
#include <kernel/Memory/Heap.h>
 | 
			
		||||
#include <kernel/LockGuard.h>
 | 
			
		||||
#include <LibELF/LoadableELF.h>
 | 
			
		||||
#include <LibELF/Values.h>
 | 
			
		||||
 | 
			
		||||
namespace LibELF
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	using namespace Kernel;
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
 | 
			
		||||
	{
 | 
			
		||||
		auto* elf_ptr = new LoadableELF(page_table, inode);
 | 
			
		||||
		if (elf_ptr == nullptr)
 | 
			
		||||
			return BAN::Error::from_errno(ENOMEM);
 | 
			
		||||
		auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
 | 
			
		||||
		TRY(elf->initialize());
 | 
			
		||||
		return BAN::move(elf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
 | 
			
		||||
		: m_inode(inode)
 | 
			
		||||
		, m_page_table(page_table)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LoadableELF::~LoadableELF()
 | 
			
		||||
	{
 | 
			
		||||
		for (const auto& program_header : m_program_headers)
 | 
			
		||||
		{
 | 
			
		||||
			switch (program_header.p_type)
 | 
			
		||||
			{
 | 
			
		||||
				case PT_NULL:
 | 
			
		||||
					continue;
 | 
			
		||||
				case PT_LOAD:
 | 
			
		||||
				{
 | 
			
		||||
					vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
 | 
			
		||||
					size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
 | 
			
		||||
					for (size_t i = 0; i < pages; i++)
 | 
			
		||||
					{
 | 
			
		||||
						paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
 | 
			
		||||
						if (paddr != 0)
 | 
			
		||||
							Heap::get().release_page(paddr);
 | 
			
		||||
					}
 | 
			
		||||
					m_page_table.unmap_range(start, pages * PAGE_SIZE);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
					ASSERT_NOT_REACHED();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> LoadableELF::initialize()
 | 
			
		||||
	{
 | 
			
		||||
		if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Too small file");
 | 
			
		||||
			return BAN::Error::from_errno(ENOEXEC);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		size_t nread = TRY(m_inode->read(0, &m_file_header, sizeof(m_file_header)));
 | 
			
		||||
		ASSERT(nread == sizeof(m_file_header));
 | 
			
		||||
 | 
			
		||||
		if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 || 
 | 
			
		||||
			m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
 | 
			
		||||
			m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
 | 
			
		||||
			m_file_header.e_ident[EI_MAG3] != ELFMAG3)
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Invalid magic in header");
 | 
			
		||||
			return BAN::Error::from_errno(ENOEXEC);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Only little-endian is supported");
 | 
			
		||||
			return BAN::Error::from_errno(ENOEXEC);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Invalid version");
 | 
			
		||||
			return BAN::Error::from_errno(ENOEXEC);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
#if ARCH(i386)
 | 
			
		||||
		if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
 | 
			
		||||
#elif ARCH(x86_64)
 | 
			
		||||
		if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
 | 
			
		||||
#endif
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Not in native format");	
 | 
			
		||||
			return BAN::Error::from_errno(EINVAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_file_header.e_type != ET_EXEC)
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Only executable files are supported");
 | 
			
		||||
			return BAN::Error::from_errno(EINVAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (m_file_header.e_version != EV_CURRENT)
 | 
			
		||||
		{
 | 
			
		||||
			dprintln("Unsupported version");
 | 
			
		||||
			return BAN::Error::from_errno(EINVAL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
 | 
			
		||||
 | 
			
		||||
		TRY(m_program_headers.resize(m_file_header.e_phnum));
 | 
			
		||||
		for (size_t i = 0; i < m_file_header.e_phnum; i++)
 | 
			
		||||
		{
 | 
			
		||||
			TRY(m_inode->read(m_file_header.e_phoff + m_file_header.e_phentsize * i, &m_program_headers[i], m_file_header.e_phentsize));
 | 
			
		||||
 | 
			
		||||
			const auto& pheader = m_program_headers[i];
 | 
			
		||||
			if (pheader.p_type != PT_NULL && pheader.p_type != PT_LOAD)
 | 
			
		||||
			{
 | 
			
		||||
				dprintln("Unsupported program header type {}", pheader.p_type);
 | 
			
		||||
				return BAN::Error::from_errno(ENOTSUP);
 | 
			
		||||
			}
 | 
			
		||||
			if (pheader.p_memsz < pheader.p_filesz)
 | 
			
		||||
			{
 | 
			
		||||
				dprintln("Invalid program header");
 | 
			
		||||
				return BAN::Error::from_errno(EINVAL);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vaddr_t LoadableELF::entry_point() const
 | 
			
		||||
	{
 | 
			
		||||
		return m_file_header.e_entry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool LoadableELF::contains(vaddr_t address) const
 | 
			
		||||
	{
 | 
			
		||||
		for (const auto& program_header : m_program_headers)
 | 
			
		||||
		{
 | 
			
		||||
			switch (program_header.p_type)
 | 
			
		||||
			{
 | 
			
		||||
				case PT_NULL:
 | 
			
		||||
					continue;
 | 
			
		||||
				case PT_LOAD:
 | 
			
		||||
					if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
 | 
			
		||||
						return true;
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					ASSERT_NOT_REACHED();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void LoadableELF::reserve_address_space()
 | 
			
		||||
	{
 | 
			
		||||
		for (const auto& program_header : m_program_headers)
 | 
			
		||||
		{
 | 
			
		||||
			switch (program_header.p_type)
 | 
			
		||||
			{
 | 
			
		||||
				case PT_NULL:
 | 
			
		||||
					break;
 | 
			
		||||
				case PT_LOAD:
 | 
			
		||||
				{
 | 
			
		||||
					vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
 | 
			
		||||
					size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
 | 
			
		||||
					ASSERT(m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE));
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
					ASSERT_NOT_REACHED();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
 | 
			
		||||
	{
 | 
			
		||||
		for (const auto& program_header : m_program_headers)
 | 
			
		||||
		{
 | 
			
		||||
			switch (program_header.p_type)
 | 
			
		||||
			{
 | 
			
		||||
				case PT_NULL:
 | 
			
		||||
					break;
 | 
			
		||||
				case PT_LOAD:
 | 
			
		||||
				{
 | 
			
		||||
					if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
 | 
			
		||||
						continue;
 | 
			
		||||
 | 
			
		||||
					PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
 | 
			
		||||
					if (program_header.p_flags & LibELF::PF_W)
 | 
			
		||||
						flags |= PageTable::Flags::ReadWrite;
 | 
			
		||||
					if (program_header.p_flags & LibELF::PF_X)
 | 
			
		||||
						flags |= PageTable::Flags::Execute;
 | 
			
		||||
 | 
			
		||||
					vaddr_t vaddr = address & PAGE_ADDR_MASK;
 | 
			
		||||
					paddr_t paddr = Heap::get().take_free_page();
 | 
			
		||||
					if (paddr == 0)
 | 
			
		||||
						return BAN::Error::from_errno(ENOMEM);
 | 
			
		||||
 | 
			
		||||
					m_page_table.map_page_at(paddr, vaddr, flags);
 | 
			
		||||
 | 
			
		||||
					memset((void*)vaddr, 0x00, PAGE_SIZE);
 | 
			
		||||
					
 | 
			
		||||
					if (vaddr / PAGE_SIZE < BAN::Math::div_round_up<size_t>(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE))
 | 
			
		||||
					{
 | 
			
		||||
						size_t vaddr_offset = 0;
 | 
			
		||||
						if (vaddr < program_header.p_vaddr)
 | 
			
		||||
							vaddr_offset = program_header.p_vaddr - vaddr;
 | 
			
		||||
						
 | 
			
		||||
						size_t file_offset = 0;
 | 
			
		||||
						if (vaddr > program_header.p_vaddr)
 | 
			
		||||
							file_offset = vaddr - program_header.p_vaddr;
 | 
			
		||||
 | 
			
		||||
						size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
 | 
			
		||||
						TRY(m_inode->read(program_header.p_offset + file_offset, (void*)(vaddr + vaddr_offset), bytes));
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					return {};
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
					ASSERT_NOT_REACHED();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ASSERT_NOT_REACHED();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
 | 
			
		||||
	{
 | 
			
		||||
		auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
 | 
			
		||||
		if (elf_ptr == nullptr)
 | 
			
		||||
			return BAN::Error::from_errno(ENOMEM);
 | 
			
		||||
		auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
 | 
			
		||||
 | 
			
		||||
		memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
 | 
			
		||||
 | 
			
		||||
		TRY(elf->m_program_headers.resize(m_program_headers.size()));
 | 
			
		||||
		memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
 | 
			
		||||
 | 
			
		||||
		elf->reserve_address_space();
 | 
			
		||||
 | 
			
		||||
		ASSERT(&PageTable::current() == &m_page_table);
 | 
			
		||||
		LockGuard _(m_page_table);
 | 
			
		||||
		ASSERT(m_page_table.is_page_free(0));
 | 
			
		||||
 | 
			
		||||
		for (const auto& program_header : m_program_headers)
 | 
			
		||||
		{
 | 
			
		||||
			switch (program_header.p_type)
 | 
			
		||||
			{
 | 
			
		||||
				case PT_NULL:
 | 
			
		||||
					break;
 | 
			
		||||
				case PT_LOAD:
 | 
			
		||||
				{
 | 
			
		||||
					PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
 | 
			
		||||
					if (program_header.p_flags & LibELF::PF_W)
 | 
			
		||||
						flags |= PageTable::Flags::ReadWrite;
 | 
			
		||||
					if (program_header.p_flags & LibELF::PF_X)
 | 
			
		||||
						flags |= PageTable::Flags::Execute;
 | 
			
		||||
 | 
			
		||||
					vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
 | 
			
		||||
					size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
 | 
			
		||||
 | 
			
		||||
					for (size_t i = 0; i < pages; i++)
 | 
			
		||||
					{
 | 
			
		||||
						if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
 | 
			
		||||
							continue;
 | 
			
		||||
 | 
			
		||||
						paddr_t paddr = Heap::get().take_free_page();
 | 
			
		||||
						if (paddr == 0)
 | 
			
		||||
							return BAN::Error::from_errno(ENOMEM);
 | 
			
		||||
 | 
			
		||||
						m_page_table.map_page_at(paddr, 0, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
 | 
			
		||||
						memcpy((void*)0, (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
 | 
			
		||||
						m_page_table.unmap_page(0);
 | 
			
		||||
 | 
			
		||||
						new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
					ASSERT_NOT_REACHED();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return elf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#ifndef __is_kernel
 | 
			
		||||
#error "This is kernel only header"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <BAN/UniqPtr.h>
 | 
			
		||||
#include <BAN/Vector.h>
 | 
			
		||||
 | 
			
		||||
#include <kernel/FS/Inode.h>
 | 
			
		||||
#include <kernel/Memory/PageTable.h>
 | 
			
		||||
 | 
			
		||||
#include <LibELF/Types.h>
 | 
			
		||||
 | 
			
		||||
namespace LibELF
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	class LoadableELF
 | 
			
		||||
	{
 | 
			
		||||
		BAN_NON_COPYABLE(LoadableELF);
 | 
			
		||||
		BAN_NON_MOVABLE(LoadableELF);
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
 | 
			
		||||
		~LoadableELF();
 | 
			
		||||
 | 
			
		||||
		Kernel::vaddr_t entry_point() const;
 | 
			
		||||
 | 
			
		||||
		bool contains(Kernel::vaddr_t address) const;
 | 
			
		||||
		void reserve_address_space();
 | 
			
		||||
 | 
			
		||||
		BAN::ErrorOr<void> load_page_to_memory(Kernel::vaddr_t address);
 | 
			
		||||
 | 
			
		||||
		BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> clone(Kernel::PageTable&);
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
 | 
			
		||||
		BAN::ErrorOr<void> initialize();
 | 
			
		||||
 | 
			
		||||
	private:
 | 
			
		||||
		BAN::RefPtr<Kernel::Inode>			m_inode;
 | 
			
		||||
		Kernel::PageTable&					m_page_table;
 | 
			
		||||
		ElfNativeFileHeader					m_file_header;
 | 
			
		||||
		BAN::Vector<ElfNativeProgramHeader>	m_program_headers;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue