LibELF: Start implementing elf library
This commit is contained in:
parent
60a99d1d23
commit
079df39ca8
|
@ -28,6 +28,7 @@ set(DISK_IMAGE_PATH ${CMAKE_BINARY_DIR}/banan-os.img)
|
||||||
add_subdirectory(kernel)
|
add_subdirectory(kernel)
|
||||||
add_subdirectory(BAN)
|
add_subdirectory(BAN)
|
||||||
add_subdirectory(libc)
|
add_subdirectory(libc)
|
||||||
|
add_subdirectory(LibELF)
|
||||||
|
|
||||||
add_custom_target(sysroot
|
add_custom_target(sysroot
|
||||||
COMMAND mkdir -p ${BANAN_SYSROOT}
|
COMMAND mkdir -p ${BANAN_SYSROOT}
|
||||||
|
@ -41,6 +42,7 @@ add_custom_target(headers
|
||||||
DEPENDS kernel-headers
|
DEPENDS kernel-headers
|
||||||
DEPENDS ban-headers
|
DEPENDS ban-headers
|
||||||
DEPENDS libc-headers
|
DEPENDS libc-headers
|
||||||
|
DEPENDS libelf-headers
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(toolchain
|
add_custom_target(toolchain
|
||||||
|
@ -50,6 +52,7 @@ add_custom_target(toolchain
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(image
|
add_custom_target(image
|
||||||
|
COMMAND ${CMAKE_CXX_COMPILER} -x c ${CMAKE_SOURCE_DIR}/userspace/test.c -o ${BANAN_SYSROOT}/bin/test
|
||||||
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image.sh
|
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image.sh
|
||||||
DEPENDS kernel-install
|
DEPENDS kernel-install
|
||||||
DEPENDS ban-install
|
DEPENDS ban-install
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
project(LibELF CXX)
|
||||||
|
|
||||||
|
add_custom_target(libelf-headers
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include ${BANAN_INCLUDE}
|
||||||
|
DEPENDS sysroot
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(libelf-install
|
||||||
|
DEPENDS libelf-headers
|
||||||
|
)
|
|
@ -0,0 +1,189 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <kernel/Process.h>
|
||||||
|
#include <LibELF/ELF.h>
|
||||||
|
#include <LibELF/Values.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
namespace LibELF
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<ELF*> ELF::load_from_file(BAN::StringView file_path)
|
||||||
|
{
|
||||||
|
ELF* elf = nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
BAN::Vector<uint8_t> data;
|
||||||
|
|
||||||
|
int fd = TRY(Kernel::Process::current()->open(file_path, O_RDONLY));
|
||||||
|
BAN::ScopeGuard _([fd] { MUST(Kernel::Process::current()->close(fd)); });
|
||||||
|
|
||||||
|
stat st;
|
||||||
|
TRY(Kernel::Process::current()->fstat(fd, &st));
|
||||||
|
|
||||||
|
TRY(data.resize(st.st_size));
|
||||||
|
|
||||||
|
TRY(Kernel::Process::current()->read(fd, data.data(), data.size()));
|
||||||
|
|
||||||
|
elf = new ELF(BAN::move(data));
|
||||||
|
ASSERT(elf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto res = elf->load(); res.is_error())
|
||||||
|
{
|
||||||
|
delete elf;
|
||||||
|
return res.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return elf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ELF::lookup_section_name(uint32_t offset) const
|
||||||
|
{
|
||||||
|
return lookup_string(file_header64().e_shstrndx, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ELF::lookup_string(size_t table_index, uint32_t offset) const
|
||||||
|
{
|
||||||
|
if (table_index == SHN_UNDEF)
|
||||||
|
return nullptr;
|
||||||
|
auto& section_header = section_header64(table_index);
|
||||||
|
return (const char*)m_data.data() + section_header.sh_offset + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ELF::parse_elf64_file_header(const Elf64FileHeader& header)
|
||||||
|
{
|
||||||
|
if (header.e_type != ET_EXEC)
|
||||||
|
{
|
||||||
|
dprintln("Only executable files are supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.e_version != EV_CURRENT)
|
||||||
|
{
|
||||||
|
dprintln("Invalid ELF version");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ELF::parse_elf64_section_header(const Elf64SectionHeader& header)
|
||||||
|
{
|
||||||
|
if (auto* name = lookup_section_name(header.sh_name))
|
||||||
|
dprintln("{}", name);
|
||||||
|
|
||||||
|
switch (header.sh_type)
|
||||||
|
{
|
||||||
|
case SHT_NULL:
|
||||||
|
dprintln(" SHT_NULL");
|
||||||
|
break;
|
||||||
|
case SHT_PROGBITS:
|
||||||
|
dprintln(" SHT_PROGBITS");
|
||||||
|
break;
|
||||||
|
case SHT_SYMTAB:
|
||||||
|
for (size_t i = 1; i < header.sh_size / header.sh_entsize; i++)
|
||||||
|
{
|
||||||
|
auto& symbol = ((const Elf64Symbol*)(m_data.data() + header.sh_offset))[i];
|
||||||
|
if (auto* name = lookup_string(header.sh_link, symbol.st_name))
|
||||||
|
dprintln(" {}", name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHT_STRTAB:
|
||||||
|
dprintln(" SHT_STRTAB");
|
||||||
|
break;
|
||||||
|
case SHT_RELA:
|
||||||
|
dprintln(" SHT_RELA");
|
||||||
|
break;
|
||||||
|
case SHT_NOBITS:
|
||||||
|
dprintln(" SHT_NOBITS");
|
||||||
|
break;
|
||||||
|
case SHT_REL:
|
||||||
|
dprintln(" SHT_REL");
|
||||||
|
break;
|
||||||
|
case SHT_SHLIB:
|
||||||
|
dprintln(" SHT_SHLIB");
|
||||||
|
break;
|
||||||
|
case SHT_DYNSYM:
|
||||||
|
dprintln(" SHT_DYNSYM");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> ELF::load()
|
||||||
|
{
|
||||||
|
if (m_data.size() < EI_NIDENT)
|
||||||
|
{
|
||||||
|
dprintln("Too small ELF file");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_data[EI_MAG0] != ELFMAG0 ||
|
||||||
|
m_data[EI_MAG1] != ELFMAG1 ||
|
||||||
|
m_data[EI_MAG2] != ELFMAG2 ||
|
||||||
|
m_data[EI_MAG3] != ELFMAG3)
|
||||||
|
{
|
||||||
|
dprintln("Invalid ELF header");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_data[EI_DATA] != ELFDATA2LSB)
|
||||||
|
{
|
||||||
|
dprintln("Only little-endian is supported");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_data[EI_VERSION] != EV_CURRENT)
|
||||||
|
{
|
||||||
|
dprintln("Invalid ELF version");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_data[EI_CLASS] != ELFCLASS64)
|
||||||
|
{
|
||||||
|
dprintln("Only 64 bit is supported");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_data.size() <= sizeof(Elf64FileHeader))
|
||||||
|
{
|
||||||
|
dprintln("Too small ELF file");
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& header = file_header64();
|
||||||
|
if (!parse_elf64_file_header(header))
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
for (size_t i = 1; i < header.e_shnum; i++)
|
||||||
|
{
|
||||||
|
auto& section_header = section_header64(i);
|
||||||
|
if (!parse_elf64_section_header(section_header))
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Elf64FileHeader& ELF::file_header64() const
|
||||||
|
{
|
||||||
|
return *(const Elf64FileHeader*)m_data.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Elf64ProgramHeader& ELF::program_header64(size_t index) const
|
||||||
|
{
|
||||||
|
return ((const Elf64ProgramHeader*)(m_data.data() + file_header64().e_phoff))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Elf64SectionHeader& ELF::section_header64(size_t index) const
|
||||||
|
{
|
||||||
|
return ((const Elf64SectionHeader*)(m_data.data() + file_header64().e_shoff))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
namespace LibELF
|
||||||
|
{
|
||||||
|
|
||||||
|
class ELF
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<ELF*> load_from_file(BAN::StringView);
|
||||||
|
|
||||||
|
const Elf64FileHeader& file_header64() const;
|
||||||
|
const Elf64ProgramHeader& program_header64(size_t) const;
|
||||||
|
const Elf64SectionHeader& section_header64(size_t) const;
|
||||||
|
|
||||||
|
const char* lookup_section_name(uint32_t) const;
|
||||||
|
const char* lookup_string(size_t, uint32_t) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ELF(BAN::Vector<uint8_t>&& data)
|
||||||
|
: m_data(BAN::move(data))
|
||||||
|
{}
|
||||||
|
BAN::ErrorOr<void> load();
|
||||||
|
|
||||||
|
bool parse_elf64_file_header(const Elf64FileHeader&);
|
||||||
|
bool parse_elf64_section_header(const Elf64SectionHeader&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BAN::Vector<uint8_t> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace LibELF
|
||||||
|
{
|
||||||
|
|
||||||
|
using Elf64Addr = uint64_t;
|
||||||
|
using Elf64Off = uint64_t;
|
||||||
|
using Elf64Half = uint16_t;
|
||||||
|
using Elf64Word = uint32_t;
|
||||||
|
using Elf64Sword = int32_t;
|
||||||
|
using Elf64Xword = uint64_t;
|
||||||
|
using Elf64Sxword = int64_t;
|
||||||
|
|
||||||
|
struct Elf64FileHeader
|
||||||
|
{
|
||||||
|
unsigned char e_ident[16];
|
||||||
|
Elf64Half e_type;
|
||||||
|
Elf64Half e_machine;
|
||||||
|
Elf64Word e_version;
|
||||||
|
Elf64Addr e_entry;
|
||||||
|
Elf64Off e_phoff;
|
||||||
|
Elf64Off e_shoff;
|
||||||
|
Elf64Word e_flags;
|
||||||
|
Elf64Half e_ehsize;
|
||||||
|
Elf64Half e_phentsize;
|
||||||
|
Elf64Half e_phnum;
|
||||||
|
Elf64Half e_shentsize;
|
||||||
|
Elf64Half e_shnum;
|
||||||
|
Elf64Half e_shstrndx;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Elf64SectionHeader
|
||||||
|
{
|
||||||
|
Elf64Word sh_name;
|
||||||
|
Elf64Word sh_type;
|
||||||
|
Elf64Xword sh_flags;
|
||||||
|
Elf64Addr sh_addr;
|
||||||
|
Elf64Off sh_offset;
|
||||||
|
Elf64Xword sh_size;
|
||||||
|
Elf64Word sh_link;
|
||||||
|
Elf64Word sh_info;
|
||||||
|
Elf64Xword sh_addralign;
|
||||||
|
Elf64Xword sh_entsize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Elf64Symbol
|
||||||
|
{
|
||||||
|
Elf64Word st_name;
|
||||||
|
unsigned char st_info;
|
||||||
|
unsigned char st_other;
|
||||||
|
Elf64Half st_shndx;
|
||||||
|
Elf64Addr st_value;
|
||||||
|
Elf64Xword st_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Elf64Relocation
|
||||||
|
{
|
||||||
|
Elf64Addr r_offset;
|
||||||
|
Elf64Xword r_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Elf64RelocationA
|
||||||
|
{
|
||||||
|
Elf64Addr r_offset;
|
||||||
|
Elf64Xword r_info;
|
||||||
|
Elf64Sxword r_addend;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Elf64ProgramHeader
|
||||||
|
{
|
||||||
|
Elf64Word p_type;
|
||||||
|
Elf64Word p_flags;
|
||||||
|
Elf64Off p_offset;
|
||||||
|
Elf64Addr p_vaddr;
|
||||||
|
Elf64Addr p_paddr;
|
||||||
|
Elf64Xword p_filesz;
|
||||||
|
Elf64Xword p_memsz;
|
||||||
|
Elf64Xword p_align;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace LibELF
|
||||||
|
{
|
||||||
|
|
||||||
|
enum ELF_Ident
|
||||||
|
{
|
||||||
|
ELFMAG0 = 0x7F,
|
||||||
|
ELFMAG1 = 'E',
|
||||||
|
ELFMAG2 = 'L',
|
||||||
|
ELFMAG3 = 'F',
|
||||||
|
|
||||||
|
ELFCLASSNONE = 0,
|
||||||
|
ELFCLASS32 = 1,
|
||||||
|
ELFCLASS64 = 2,
|
||||||
|
|
||||||
|
ELFDATANONE = 0,
|
||||||
|
ELFDATA2LSB = 1,
|
||||||
|
ELFDATA2MSB = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_EI
|
||||||
|
{
|
||||||
|
EI_MAG0 = 0,
|
||||||
|
EI_MAG1 = 1,
|
||||||
|
EI_MAG2 = 2,
|
||||||
|
EI_MAG3 = 3,
|
||||||
|
EI_CLASS = 4,
|
||||||
|
EI_DATA = 5,
|
||||||
|
EI_VERSION = 6,
|
||||||
|
EI_OSABI = 7,
|
||||||
|
EI_ABIVERSION = 8,
|
||||||
|
EI_NIDENT = 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_ET
|
||||||
|
{
|
||||||
|
ET_NONE = 0,
|
||||||
|
ET_REL = 1,
|
||||||
|
ET_EXEC = 2,
|
||||||
|
ET_DYN = 3,
|
||||||
|
ET_CORE = 4,
|
||||||
|
ET_LOOS = 0xfe00,
|
||||||
|
ET_HIOS = 0xfeff,
|
||||||
|
ET_LOPROC = 0xff00,
|
||||||
|
ET_HIPROC = 0xffff,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_EV
|
||||||
|
{
|
||||||
|
EV_NONE = 0,
|
||||||
|
EV_CURRENT = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_SHT
|
||||||
|
{
|
||||||
|
SHT_NULL = 0,
|
||||||
|
SHT_PROGBITS = 1,
|
||||||
|
SHT_SYMTAB = 2,
|
||||||
|
SHT_STRTAB = 3,
|
||||||
|
SHT_RELA = 4,
|
||||||
|
SHT_NOBITS = 8,
|
||||||
|
SHT_REL = 9,
|
||||||
|
SHT_SHLIB = 10,
|
||||||
|
SHT_DYNSYM = 11,
|
||||||
|
SHT_LOOS = 0x60000000,
|
||||||
|
SHT_HIOS = 0x6FFFFFFF,
|
||||||
|
SHT_LOPROC = 0x70000000,
|
||||||
|
SHT_HIPROC = 0x7FFFFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_SHF
|
||||||
|
{
|
||||||
|
SHF_WRITE = 0x1,
|
||||||
|
SHF_ALLOC = 0x2,
|
||||||
|
SHF_EXECINSTR = 0x4,
|
||||||
|
SHF_MASKOS = 0x0F000000,
|
||||||
|
SHF_MASKPROC = 0xF0000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_SHN
|
||||||
|
{
|
||||||
|
SHN_UNDEF = 0,
|
||||||
|
SHN_LOPROC = 0xFF00,
|
||||||
|
SHN_HIPROC = 0xFF1F,
|
||||||
|
SHN_LOOS = 0xFF20,
|
||||||
|
SHN_HIOS = 0xFF3F,
|
||||||
|
SHN_ABS = 0xFFF1,
|
||||||
|
SHN_COMMON = 0xFFF2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_STB
|
||||||
|
{
|
||||||
|
STB_LOCAL = 0,
|
||||||
|
STB_GLOBAL = 1,
|
||||||
|
STB_WEAK = 2,
|
||||||
|
STB_LOOS = 10,
|
||||||
|
STB_HIOS = 12,
|
||||||
|
STB_LOPROC = 13,
|
||||||
|
STB_HIPROC = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_STT
|
||||||
|
{
|
||||||
|
STT_NOTYPE = 0,
|
||||||
|
STT_OBJECT = 1,
|
||||||
|
STT_FUNC = 2,
|
||||||
|
STT_SECTION = 3,
|
||||||
|
STT_FILE = 4,
|
||||||
|
STT_LOOS = 10,
|
||||||
|
STT_HIOS = 12,
|
||||||
|
STT_LOPROC = 13,
|
||||||
|
STT_HIPROC = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_PT
|
||||||
|
{
|
||||||
|
PT_NULL = 0,
|
||||||
|
PT_LOAD = 1,
|
||||||
|
PT_DYNAMIC = 2,
|
||||||
|
PT_INTERP = 3,
|
||||||
|
PT_NOTE = 4,
|
||||||
|
PT_SHLIB = 5,
|
||||||
|
PT_PHDR = 6,
|
||||||
|
PT_LOOS = 0x60000000,
|
||||||
|
PT_HIOS = 0x6FFFFFFF,
|
||||||
|
PT_LOPROC = 0x70000000,
|
||||||
|
PT_HIPROC = 0x7FFFFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ELF_PF
|
||||||
|
{
|
||||||
|
PF_X = 0x1,
|
||||||
|
PF_W = 0x2,
|
||||||
|
PF_R = 0x4,
|
||||||
|
PF_MASKOS = 0x00FF0000,
|
||||||
|
PF_MASKPROC = 0xFF000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -93,10 +93,15 @@ set(LIBC_SOURCES
|
||||||
../libc/string.cpp
|
../libc/string.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(LIBELF_SOURCES
|
||||||
|
../LibELF/LibELF/ELF.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(KERNEL_SOURCES
|
set(KERNEL_SOURCES
|
||||||
${KERNEL_SOURCES}
|
${KERNEL_SOURCES}
|
||||||
${BAN_SOURCES}
|
${BAN_SOURCES}
|
||||||
${LIBC_SOURCES}
|
${LIBC_SOURCES}
|
||||||
|
${LIBELF_SOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(kernel ${KERNEL_SOURCES})
|
add_executable(kernel ${KERNEL_SOURCES})
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include <kernel/Terminal/TTY.h>
|
#include <kernel/Terminal/TTY.h>
|
||||||
#include <kernel/Terminal/VesaTerminalDriver.h>
|
#include <kernel/Terminal/VesaTerminalDriver.h>
|
||||||
|
|
||||||
|
#include <LibELF/ELF.h>
|
||||||
|
|
||||||
extern "C" const char g_kernel_cmdline[];
|
extern "C" const char g_kernel_cmdline[];
|
||||||
|
|
||||||
struct ParsedCommandLine
|
struct ParsedCommandLine
|
||||||
|
@ -177,7 +179,17 @@ static void init2(void* terminal_driver)
|
||||||
ASSERT(tty1);
|
ASSERT(tty1);
|
||||||
DeviceManager::get().add_device(tty1);
|
DeviceManager::get().add_device(tty1);
|
||||||
|
|
||||||
return jump_userspace();
|
MUST(Process::create_kernel(
|
||||||
|
[](void*)
|
||||||
|
{
|
||||||
|
MUST(LibELF::ELF::load_from_file("/bin/test"sv));
|
||||||
|
Process::current()->exit();
|
||||||
|
}, nullptr
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
|
||||||
|
jump_userspace();
|
||||||
|
return;
|
||||||
|
|
||||||
MUST(Process::create_kernel(
|
MUST(Process::create_kernel(
|
||||||
[](void*)
|
[](void*)
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue