Kernel: Allow loading PIEs
This is initial work towards implementing dynamic loader and shared library support.
This commit is contained in:
parent
2c520391eb
commit
5121d0d934
|
@ -1,6 +1,7 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
|
||||||
#include <kernel/Lock/LockGuard.h>
|
#include <kernel/Lock/LockGuard.h>
|
||||||
|
#include <kernel/Memory/Heap.h>
|
||||||
|
#include <kernel/Random.h>
|
||||||
#include <LibELF/LoadableELF.h>
|
#include <LibELF/LoadableELF.h>
|
||||||
#include <LibELF/Values.h>
|
#include <LibELF/Values.h>
|
||||||
|
|
||||||
|
@ -96,10 +97,10 @@ namespace LibELF
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_file_header.e_type != ET_EXEC)
|
if (m_file_header.e_type != ET_EXEC && m_file_header.e_type != ET_DYN)
|
||||||
{
|
{
|
||||||
dprintln("Only executable files are supported");
|
dprintln("Unsupported file header type {}", m_file_header.e_type);
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_file_header.e_version != EV_CURRENT)
|
if (m_file_header.e_version != EV_CURRENT)
|
||||||
|
@ -110,24 +111,36 @@ namespace LibELF
|
||||||
|
|
||||||
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
|
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
|
||||||
|
|
||||||
TRY(m_program_headers.resize(m_file_header.e_phnum));
|
BAN::Vector<uint8_t> pheader_buffer;
|
||||||
|
TRY(pheader_buffer.resize(m_file_header.e_phnum * m_file_header.e_phentsize));
|
||||||
|
TRY(m_inode->read(m_file_header.e_phoff, BAN::ByteSpan(pheader_buffer.span())));
|
||||||
|
|
||||||
|
const vaddr_t dyn_base = (m_file_header.e_type == ET_DYN) ? 0x40000000 + (Random::get_u32() & 0x3FFFF000) : 0;
|
||||||
|
m_file_header.e_entry += dyn_base;
|
||||||
|
|
||||||
for (size_t i = 0; i < m_file_header.e_phnum; i++)
|
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, BAN::ByteSpan::from(m_program_headers[i])));
|
const auto& pheader = *reinterpret_cast<ElfNativeProgramHeader*>(pheader_buffer.data() + 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)
|
if (pheader.p_memsz < pheader.p_filesz)
|
||||||
{
|
{
|
||||||
dprintln("Invalid program header");
|
dprintln("Invalid program header");
|
||||||
return BAN::Error::from_errno(EINVAL);
|
return BAN::Error::from_errno(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
|
switch (pheader.p_type)
|
||||||
|
{
|
||||||
|
case PT_NULL:
|
||||||
|
case PT_DYNAMIC:
|
||||||
|
break;
|
||||||
|
case PT_LOAD:
|
||||||
|
TRY(m_program_headers.push_back(pheader));
|
||||||
|
m_program_headers.back().p_vaddr += dyn_base;
|
||||||
|
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dprintln("Unsupported program header type {}", pheader.p_type);
|
||||||
|
return BAN::Error::from_errno(ENOTSUP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -142,17 +155,9 @@ namespace LibELF
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
for (const auto& program_header : m_program_headers)
|
||||||
{
|
{
|
||||||
switch (program_header.p_type)
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
{
|
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
|
||||||
case PT_NULL:
|
return true;
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -161,21 +166,11 @@ namespace LibELF
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
for (const auto& program_header : m_program_headers)
|
||||||
{
|
{
|
||||||
switch (program_header.p_type)
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
{
|
const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||||
case PT_NULL:
|
const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||||
break;
|
if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE))
|
||||||
case PT_LOAD:
|
return false;
|
||||||
{
|
|
||||||
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);
|
|
||||||
if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE))
|
|
||||||
return false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -184,20 +179,11 @@ namespace LibELF
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
for (const auto& program_header : m_program_headers)
|
||||||
{
|
{
|
||||||
switch (program_header.p_type)
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
{
|
const vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
|
||||||
case PT_NULL:
|
const size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
||||||
break;
|
if (!m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE))
|
||||||
case PT_LOAD:
|
ASSERT_NOT_REACHED();
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m_loaded = true;
|
m_loaded = true;
|
||||||
}
|
}
|
||||||
|
@ -214,59 +200,50 @@ namespace LibELF
|
||||||
{
|
{
|
||||||
for (const auto& program_header : m_program_headers)
|
for (const auto& program_header : m_program_headers)
|
||||||
{
|
{
|
||||||
switch (program_header.p_type)
|
ASSERT(program_header.p_type == 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;
|
||||||
|
|
||||||
|
const vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
||||||
|
const paddr_t paddr = Heap::get().take_free_page();
|
||||||
|
if (paddr == 0)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
|
||||||
|
// Temporarily map page as RW so kernel can write to it
|
||||||
|
m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
||||||
|
m_physical_page_count++;
|
||||||
|
|
||||||
|
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))
|
||||||
{
|
{
|
||||||
case PT_NULL:
|
size_t vaddr_offset = 0;
|
||||||
break;
|
if (vaddr < program_header.p_vaddr)
|
||||||
case PT_LOAD:
|
vaddr_offset = program_header.p_vaddr - vaddr;
|
||||||
{
|
|
||||||
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;
|
size_t file_offset = 0;
|
||||||
if (program_header.p_flags & LibELF::PF_W)
|
if (vaddr > program_header.p_vaddr)
|
||||||
flags |= PageTable::Flags::ReadWrite;
|
file_offset = vaddr - program_header.p_vaddr;
|
||||||
if (program_header.p_flags & LibELF::PF_X)
|
|
||||||
flags |= PageTable::Flags::Execute;
|
|
||||||
|
|
||||||
vaddr_t vaddr = address & PAGE_ADDR_MASK;
|
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
|
||||||
paddr_t paddr = Heap::get().take_free_page();
|
TRY(m_inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes }));
|
||||||
if (paddr == 0)
|
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
|
||||||
|
|
||||||
// Temporarily map page as RW so kernel can write to it
|
|
||||||
m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
|
|
||||||
m_physical_page_count++;
|
|
||||||
|
|
||||||
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, { (uint8_t*)vaddr + vaddr_offset, bytes }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map page with the correct flags
|
|
||||||
m_page_table.map_page_at(paddr, vaddr, flags);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map page with the correct flags
|
||||||
|
m_page_table.map_page_at(paddr, vaddr, flags);
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
|
||||||
{
|
{
|
||||||
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
|
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
|
||||||
|
@ -283,45 +260,35 @@ namespace LibELF
|
||||||
|
|
||||||
for (const auto& program_header : m_program_headers)
|
for (const auto& program_header : m_program_headers)
|
||||||
{
|
{
|
||||||
switch (program_header.p_type)
|
ASSERT(program_header.p_type == PT_LOAD);
|
||||||
|
|
||||||
|
if (!(program_header.p_flags & LibELF::PF_W))
|
||||||
|
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 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++)
|
||||||
{
|
{
|
||||||
case PT_NULL:
|
if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
|
||||||
break;
|
continue;
|
||||||
case PT_LOAD:
|
|
||||||
{
|
|
||||||
if (!(program_header.p_flags & LibELF::PF_W))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
|
paddr_t paddr = Heap::get().take_free_page();
|
||||||
if (program_header.p_flags & LibELF::PF_W)
|
if (paddr == 0)
|
||||||
flags |= PageTable::Flags::ReadWrite;
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
if (program_header.p_flags & LibELF::PF_X)
|
|
||||||
flags |= PageTable::Flags::Execute;
|
|
||||||
|
|
||||||
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
|
PageTable::with_fast_page(paddr, [&] {
|
||||||
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
|
memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
|
||||||
|
});
|
||||||
|
|
||||||
for (size_t i = 0; i < pages; i++)
|
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
|
||||||
{
|
elf->m_physical_page_count++;
|
||||||
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);
|
|
||||||
|
|
||||||
PageTable::with_fast_page(paddr, [&] {
|
|
||||||
memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
|
|
||||||
});
|
|
||||||
|
|
||||||
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
|
|
||||||
elf->m_physical_page_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,16 @@ namespace LibELF
|
||||||
Elf32Word p_align;
|
Elf32Word p_align;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Elf32Dynamic
|
||||||
|
{
|
||||||
|
Elf32Sword d_tag;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Elf32Word d_val;
|
||||||
|
Elf32Addr d_ptr;
|
||||||
|
} d_un;
|
||||||
|
};
|
||||||
|
|
||||||
using Elf64Addr = uint64_t;
|
using Elf64Addr = uint64_t;
|
||||||
using Elf64Off = uint64_t;
|
using Elf64Off = uint64_t;
|
||||||
using Elf64Half = uint16_t;
|
using Elf64Half = uint16_t;
|
||||||
|
@ -155,6 +165,16 @@ namespace LibELF
|
||||||
Elf64Xword p_align;
|
Elf64Xword p_align;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Elf64Dynamic
|
||||||
|
{
|
||||||
|
Elf64Sxword d_tag;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Elf64Xword d_val;
|
||||||
|
Elf64Addr d_ptr;
|
||||||
|
} d_un;
|
||||||
|
};
|
||||||
|
|
||||||
#if ARCH(i686)
|
#if ARCH(i686)
|
||||||
using ElfNativeAddr = Elf32Addr;
|
using ElfNativeAddr = Elf32Addr;
|
||||||
using ElfNativeOff = Elf32Off;
|
using ElfNativeOff = Elf32Off;
|
||||||
|
@ -167,6 +187,7 @@ namespace LibELF
|
||||||
using ElfNativeRelocation = Elf32Relocation;
|
using ElfNativeRelocation = Elf32Relocation;
|
||||||
using ElfNativeRelocationA = Elf32RelocationA;
|
using ElfNativeRelocationA = Elf32RelocationA;
|
||||||
using ElfNativeProgramHeader = Elf32ProgramHeader;
|
using ElfNativeProgramHeader = Elf32ProgramHeader;
|
||||||
|
using ElfNativeDynamic = Elf32Dynamic;
|
||||||
#elif ARCH(x86_64)
|
#elif ARCH(x86_64)
|
||||||
using ElfNativeAddr = Elf64Addr;
|
using ElfNativeAddr = Elf64Addr;
|
||||||
using ElfNativeOff = Elf64Off;
|
using ElfNativeOff = Elf64Off;
|
||||||
|
@ -181,6 +202,7 @@ namespace LibELF
|
||||||
using ElfNativeRelocation = Elf64Relocation;
|
using ElfNativeRelocation = Elf64Relocation;
|
||||||
using ElfNativeRelocationA = Elf64RelocationA;
|
using ElfNativeRelocationA = Elf64RelocationA;
|
||||||
using ElfNativeProgramHeader = Elf64ProgramHeader;
|
using ElfNativeProgramHeader = Elf64ProgramHeader;
|
||||||
|
using ElfNativeDynamic = Elf64Dynamic;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue