#include #include #include #include #include #include #include #include namespace Kernel::ELF { using namespace LibELF; static BAN::ErrorOr read_and_validate_file_header(BAN::RefPtr inode) { if ((size_t)inode->size() < sizeof(ElfNativeFileHeader)) { dprintln("File is too small to be ELF"); return BAN::Error::from_errno(ENOEXEC); } ElfNativeFileHeader file_header; size_t nread = TRY(inode->read(0, BAN::ByteSpan::from(file_header))); ASSERT(nread == sizeof(file_header)); if (file_header.e_ident[EI_MAG0] != ELFMAG0 || file_header.e_ident[EI_MAG1] != ELFMAG1 || file_header.e_ident[EI_MAG2] != ELFMAG2 || file_header.e_ident[EI_MAG3] != ELFMAG3) { dprintln("Not an ELF file"); return BAN::Error::from_errno(ENOEXEC); } if (file_header.e_ident[EI_DATA] != ELFDATA2LSB) { dprintln("Not in little-endian"); return BAN::Error::from_errno(ENOEXEC); } if (file_header.e_ident[EI_VERSION] != EV_CURRENT) { dprintln("Unsupported version {}", file_header.e_ident[EI_VERSION]); return BAN::Error::from_errno(ENOEXEC); } #if ARCH(i686) if (file_header.e_ident[EI_CLASS] != ELFCLASS32) #elif ARCH(x86_64) if (file_header.e_ident[EI_CLASS] != ELFCLASS64) #endif { dprintln("Not in native format"); return BAN::Error::from_errno(EINVAL); } if (file_header.e_type != ET_EXEC && file_header.e_type != ET_DYN) { dprintln("Unsupported file header type {}", file_header.e_type); return BAN::Error::from_errno(ENOTSUP); } if (file_header.e_version != EV_CURRENT) { dprintln("Unsupported version {}", file_header.e_version); return BAN::Error::from_errno(EINVAL); } if (file_header.e_phentsize < sizeof(ElfNativeProgramHeader)) { dprintln("Too small program header size ({} bytes)", file_header.e_phentsize); return BAN::Error::from_errno(EINVAL); } return file_header; } static BAN::ErrorOr> read_program_headers(BAN::RefPtr inode, const ElfNativeFileHeader& file_header) { BAN::Vector program_header_buffer; TRY(program_header_buffer.resize(file_header.e_phnum * file_header.e_phentsize)); TRY(inode->read(file_header.e_phoff, BAN::ByteSpan(program_header_buffer.span()))); BAN::Vector program_headers; TRY(program_headers.reserve(file_header.e_phnum)); for (size_t i = 0; i < file_header.e_phnum; i++) { const auto& pheader = *reinterpret_cast(program_header_buffer.data() + i * file_header.e_phentsize); if (pheader.p_memsz < pheader.p_filesz) { dprintln("Invalid program header, memsz less than filesz"); return BAN::Error::from_errno(EINVAL); } MUST(program_headers.emplace_back(pheader)); } return BAN::move(program_headers); } BAN::ErrorOr load_from_inode(BAN::RefPtr inode, const Credentials& credentials, PageTable& page_table) { auto file_header = TRY(read_and_validate_file_header(inode)); auto program_headers = TRY(read_program_headers(inode, file_header)); vaddr_t executable_end = 0; BAN::String interpreter; for (const auto& program_header : program_headers) { if (program_header.p_type == PT_LOAD) executable_end = BAN::Math::max(executable_end, program_header.p_vaddr + program_header.p_memsz); else if (program_header.p_type == PT_INTERP) { BAN::Vector interp_buffer; TRY(interp_buffer.resize(program_header.p_memsz, 0)); TRY(inode->read(program_header.p_offset, BAN::ByteSpan(interp_buffer.data(), program_header.p_filesz))); if (interp_buffer.empty() || interp_buffer.front() != '/' || interp_buffer.back() != '\0') { dprintln("ELF interpreter is not an valid absolute path"); return BAN::Error::from_errno(EINVAL); } auto interpreter_sv = BAN::StringView(reinterpret_cast(interp_buffer.data()), interp_buffer.size() - 1); for (char ch : interpreter_sv) { if (isprint(ch)) continue; dprintln("ELF interpreter name contains non-printable characters"); return BAN::Error::from_errno(EINVAL); } interpreter.clear(); TRY(interpreter.append(interpreter_sv)); } } if (file_header.e_type == ET_DYN) executable_end = 0x400000; if (!interpreter.empty()) { auto interpreter_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(credentials, interpreter, O_EXEC)).inode; auto interpreter_file_header = TRY(read_and_validate_file_header(interpreter_inode)); auto interpreter_program_headers = TRY(read_program_headers(interpreter_inode, interpreter_file_header)); for (const auto& program_header : interpreter_program_headers) { if (program_header.p_type == PT_INTERP) { dprintln("ELF interpreter has an interpreter specified"); return BAN::Error::from_errno(EINVAL); } } inode = interpreter_inode; file_header = interpreter_file_header; program_headers = BAN::move(interpreter_program_headers); } const vaddr_t load_base_vaddr = [&file_header, executable_end]() -> vaddr_t { if (file_header.e_type == ET_EXEC) return 0; if (file_header.e_type == ET_DYN) return (executable_end + PAGE_SIZE - 1) & PAGE_ADDR_MASK; ASSERT_NOT_REACHED(); }(); BAN::Vector> memory_regions; for (const auto& program_header : program_headers) { if (program_header.p_type != PT_LOAD) continue; const PageTable::flags_t flags = [&program_header]() -> int { PageTable::flags_t result = PageTable::Flags::UserSupervisor; if (program_header.p_flags & PF_R) result |= PageTable::Flags::Present; if (program_header.p_flags & PF_W) result |= PageTable::Flags::ReadWrite; if (program_header.p_flags & PF_X) result |= PageTable::Flags::Execute; return result; }(); const size_t file_backed_size = [&program_header]() -> size_t { if ((program_header.p_vaddr & 0xFFF) || (program_header.p_offset & 0xFFF)) return 0; if (program_header.p_filesz == program_header.p_memsz) return program_header.p_filesz; return program_header.p_filesz & ~(uintptr_t)0xFFF; }(); const vaddr_t pheader_base = load_base_vaddr + program_header.p_vaddr; if (file_backed_size) { auto region = TRY(FileBackedRegion::create( inode, page_table, program_header.p_offset, file_backed_size, { .start = pheader_base, .end = pheader_base + file_backed_size }, MemoryRegion::Type::PRIVATE, flags )); TRY(memory_regions.emplace_back(BAN::move(region))); } if (file_backed_size < program_header.p_memsz) { const vaddr_t aligned_vaddr = pheader_base & PAGE_ADDR_MASK; auto region = TRY(MemoryBackedRegion::create( page_table, (pheader_base + program_header.p_memsz) - (aligned_vaddr + file_backed_size), { .start = aligned_vaddr + file_backed_size, .end = pheader_base + program_header.p_memsz }, MemoryRegion::Type::PRIVATE, flags )); if (file_backed_size < program_header.p_filesz) { BAN::Vector file_data_buffer; TRY(file_data_buffer.resize(program_header.p_filesz - file_backed_size)); if (TRY(inode->read(program_header.p_offset + file_backed_size, file_data_buffer.span())) != file_data_buffer.size()) return BAN::Error::from_errno(EFAULT); TRY(region->copy_data_to_region(pheader_base - aligned_vaddr, file_data_buffer.data(), file_data_buffer.size())); } TRY(memory_regions.emplace_back(BAN::move(region))); } } LoadResult result; result.has_interpreter = !interpreter.empty(); result.entry_point = load_base_vaddr + file_header.e_entry; result.regions = BAN::move(memory_regions); return BAN::move(result); } }