Launching an arbitrary ELF executable from disk works! :^)

This is so cool! It's a bit messy now with two Task constructors,
but eventually they should fold into a single constructor somehow.
This commit is contained in:
Andreas Kling 2018-10-22 15:42:39 +02:00
parent befeabd8fe
commit b824f15619
Notes: sideshowbarker 2024-07-19 18:45:05 +09:00
10 changed files with 141 additions and 16 deletions

View file

@ -25,6 +25,7 @@
#pragma once
#include "Assertions.h"
#include "OwnPtr.h"
#include "StdLib.h"

View file

@ -22,7 +22,9 @@ int puts(const char* str)
void ExecSpace::initializeBuiltins()
{
#ifndef SERENITY
m_symbols.set("puts", { (char*)puts, 0 });
#endif
}
#ifdef SERENITY
@ -86,8 +88,13 @@ char* ExecSpace::symbolPtr(const char* name)
char* ExecSpace::allocateArea(String&& name, unsigned size)
{
char* ptr = static_cast<char*>(kmalloc(size));
ASSERT(ptr);
char* ptr;
if (hookableAlloc)
ptr = static_cast<char*>(hookableAlloc(name, size));
else
ptr = static_cast<char*>(kmalloc(size));
if (size)
ASSERT(ptr);
m_areas.append(make<Area>(move(name), ptr, size));
return ptr;
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/MappedFile.h>
#include <AK/OwnPtr.h>
@ -37,6 +38,8 @@ public:
ExecSpace();
~ExecSpace();
Function<void*(const String&, size_t)> hookableAlloc;
#ifdef SERENITY
bool loadELF(ByteBuffer&&);
#else

View file

@ -161,20 +161,27 @@ bool MemoryManager::unmapRegionsForTask(Task& task)
return true;
}
bool MemoryManager::mapRegion(Task& task, Task::Region& region)
{
auto& zone = *region.zone;
for (size_t i = 0; i < zone.m_pages.size(); ++i) {
auto laddr = region.linearAddress.offset(i * PAGE_SIZE);
auto pte = ensurePTE(laddr);
pte.setPhysicalPageBase(zone.m_pages[i].get());
pte.setPresent(true);
pte.setWritable(true);
pte.setUserAllowed(!task.isRing0());
//kprintf("MM: >> Mapped L%x => P%x <<\n", laddr, zone.m_pages[i].get());
}
return true;
}
bool MemoryManager::mapRegionsForTask(Task& task)
{
for (auto& region : task.m_regions) {
auto& zone = *region->zone;
for (size_t i = 0; i < zone.m_pages.size(); ++i) {
auto laddr = region->linearAddress.offset(i * PAGE_SIZE);
auto pte = ensurePTE(laddr);
pte.setPhysicalPageBase(zone.m_pages[i].get());
pte.setPresent(true);
pte.setWritable(true);
pte.setUserAllowed(!task.isRing0());
//kprintf("MM: >> Mapped L%x => P%x <<\n", laddr, zone.m_pages[i].get());
}
if (!mapRegion(task, *region))
return false;
}
return true;
}

View file

@ -6,6 +6,7 @@
#include <AK/RetainPtr.h>
#include <AK/Vector.h>
#include <AK/HashMap.h>
#include "Task.h"
class Task;
@ -49,6 +50,7 @@ public:
// HACK: don't use this jeez :(
byte* quickMapOnePage(PhysicalAddress);
bool mapRegion(Task&, Task::Region&);
bool mapRegionsForTask(Task&);
bool unmapRegionsForTask(Task&);

View file

@ -7,6 +7,7 @@
#include "system.h"
#include <VirtualFileSystem/FileHandle.h>
#include <VirtualFileSystem/VirtualFileSystem.h>
#include <ELFLoader/ExecSpace.h>
#include "MemoryManager.h"
Task* current;
@ -103,6 +104,104 @@ Task::Region* Task::allocateRegion(size_t size, String&& name)
return m_regions.last().ptr();
}
Task* Task::create(const String& path, uid_t uid, gid_t gid)
{
auto parts = path.split('/');
if (parts.isEmpty())
return nullptr;
auto handle = VirtualFileSystem::the().open(path);
if (!handle)
return nullptr;
auto elfData = handle->readEntireFile();
if (!elfData)
return nullptr;
Task* t = new Task(parts.takeLast(), uid, gid);
ExecSpace space;
space.hookableAlloc = [&] (const String& name, size_t size) {
if (!size)
return (void*)nullptr;
size = ((size / 4096) + 1) * 4096;
Region* region = t->allocateRegion(size, String(name));
ASSERT(region);
MemoryManager::the().mapRegion(*t, *region);
return (void*)region->linearAddress.asPtr();
};
bool success = space.loadELF(move(elfData));
if (!success) {
delete t;
return nullptr;
}
t->m_tss.eip = (dword)space.symbolPtr("_start");
// Add this task to head of task list (meaning it's next to run too, ATM.)
cli();
s_tasks->prepend(t);
system.nprocess++;
kprintf("Task %u (%s) spawned @ %p\n", t->pid(), t->name().characters(), t->m_tss.eip);
sti();
return t;
}
Task::Task(String&& name, uid_t uid, gid_t gid)
: m_name(move(name))
, m_pid(next_pid++)
, m_uid(uid)
, m_gid(gid)
, m_state(Runnable)
, m_ring(Ring3)
{
m_nextRegion = LinearAddress(0x600000);
memset(&m_tss, 0, sizeof(m_tss));
memset(&m_ldtEntries, 0, sizeof(m_ldtEntries));
allocateLDT();
// Only IF is set when a task boots.
m_tss.eflags = 0x0202;
WORD codeSegment = 0x1b;
WORD dataSegment = 0x23;
WORD stackSegment = dataSegment;
m_tss.ds = dataSegment;
m_tss.es = dataSegment;
m_tss.fs = dataSegment;
m_tss.gs = dataSegment;
m_tss.ss = stackSegment;
m_tss.cs = codeSegment;
m_tss.cr3 = MemoryManager::the().pageDirectoryBase().get();
// NOTE: Each task gets 16KB of stack.
static const DWORD defaultStackSize = 16384;
auto* region = allocateRegion(defaultStackSize, "stack");
ASSERT(region);
m_stackTop = region->linearAddress.offset(defaultStackSize).get() & 0xfffffff8;
m_tss.esp = m_stackTop;
// Set up a separate stack for Ring0.
// FIXME: Don't leak this stack.
m_kernelStack = kmalloc(defaultStackSize);
DWORD ring0StackTop = ((DWORD)m_kernelStack + defaultStackSize) & 0xffffff8;
m_tss.ss0 = 0x10;
m_tss.esp0 = ring0StackTop;
// HACK: Ring2 SS in the TSS is the current PID.
m_tss.ss2 = m_pid;
m_farPtr.offset = 0x98765432;
ASSERT(m_pid);
}
Task::Task(void (*e)(), const char* n, IPC::Handle h, RingLevel ring)
: m_name(n)
, m_entry(e)
@ -241,7 +340,6 @@ void Task::sys$exit(int status)
kprintf("sys$exit: %s(%u) exit with status %d\n", name().characters(), pid(), status);
setState(Exiting);
dumpRegions();
s_tasks->remove(this);

View file

@ -16,6 +16,9 @@ class Zone;
class Task : public InlineLinkedListNode<Task> {
friend class InlineLinkedListNode<Task>;
public:
static Task* create(const String& path, uid_t, gid_t);
Task(String&& name, uid_t, gid_t);
#ifdef TASK_SANITY_CHECKS
static void checkSanity(const char* msg = nullptr);
#else

Binary file not shown.

View file

@ -27,7 +27,7 @@
#include "Console.h"
#define TEST_VFS
#define TEST_ELF_LOADER
//#define TEST_ELF_LOADER
//#define TEST_CRASHY_USER_PROCESSES
static void motd_main() NORETURN;
@ -167,6 +167,8 @@ static void init_stage2()
}
#endif
auto* idTask = Task::create("/bin/id", (uid_t)209, (gid_t)1985);
//new Task(motd_main, "motd", IPC::Handle::MotdTask, Task::Ring0);
//new Task(syscall_test_main, "syscall_test", IPC::Handle::MotdTask, Task::Ring3);

View file

@ -5,7 +5,9 @@ extern "C" int main(int, char**);
extern "C" int _start()
{
// FIXME: Pass appropriate argc/argv.
main(0, nullptr);
int status = main(0, nullptr);
Syscall::invoke(Syscall::PosixExit, status);
// Birger's birthday <3
return 20150614;