mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 03:57:40 +00:00
UserspaceEmulator: Load the target executable ELF semi-properly :^)
This patch adds a basic ELF program loader to the UserspaceEmulator and creates MMU regions for each PT_LOAD header. (Note that we don't yet respect the R/W/X flags etc.) We also turn the SoftCPU into an X86::InstructionStream and give it an EIP register so we can actually execute code by fetching memory through our MMU abstraction.
This commit is contained in:
parent
0eab5659f8
commit
ae1d14bc7a
5 changed files with 68 additions and 22 deletions
|
@ -29,6 +29,7 @@
|
||||||
#include <AK/LogStream.h>
|
#include <AK/LogStream.h>
|
||||||
#include <Kernel/API/Syscall.h>
|
#include <Kernel/API/Syscall.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace UserspaceEmulator {
|
namespace UserspaceEmulator {
|
||||||
|
@ -85,6 +86,8 @@ public:
|
||||||
*reinterpret_cast<u32*>(m_data + offset) = value;
|
*reinterpret_cast<u32*>(m_data + offset) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8* data() { return m_data; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u8* m_data { nullptr };
|
u8* m_data { nullptr };
|
||||||
};
|
};
|
||||||
|
@ -107,12 +110,26 @@ void Emulator::setup_stack()
|
||||||
m_cpu.push32(0);
|
m_cpu.push32(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Emulator::exec(X86::SimpleInstructionStream& stream, u32 base)
|
bool Emulator::load_elf(const ELF::Loader& elf)
|
||||||
|
{
|
||||||
|
elf.image().for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) {
|
||||||
|
if (program_header.type() != PT_LOAD)
|
||||||
|
return;
|
||||||
|
auto region = make<SimpleRegion>(program_header.vaddr().get(), program_header.size_in_memory());
|
||||||
|
memcpy(region->data(), program_header.raw_data(), program_header.size_in_image());
|
||||||
|
mmu().add_region(move(region));
|
||||||
|
});
|
||||||
|
|
||||||
|
m_cpu.set_eip(elf.image().entry().get());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Emulator::exec()
|
||||||
{
|
{
|
||||||
size_t offset = 0;
|
|
||||||
while (!m_shutdown) {
|
while (!m_shutdown) {
|
||||||
auto insn = X86::Instruction::from_stream(stream, true, true);
|
auto base_eip = m_cpu.eip();
|
||||||
out() << "\033[33;1m" << insn.to_string(base + offset) << "\033[0m";
|
auto insn = X86::Instruction::from_stream(m_cpu, true, true);
|
||||||
|
out() << "\033[33;1m" << insn.to_string(base_eip) << "\033[0m";
|
||||||
|
|
||||||
// FIXME: Remove this hack once it's no longer needed :^)
|
// FIXME: Remove this hack once it's no longer needed :^)
|
||||||
if (insn.mnemonic() == "RET")
|
if (insn.mnemonic() == "RET")
|
||||||
|
@ -120,8 +137,6 @@ int Emulator::exec(X86::SimpleInstructionStream& stream, u32 base)
|
||||||
|
|
||||||
(m_cpu.*insn.handler())(insn);
|
(m_cpu.*insn.handler())(insn);
|
||||||
m_cpu.dump();
|
m_cpu.dump();
|
||||||
|
|
||||||
offset += insn.length();
|
|
||||||
}
|
}
|
||||||
return m_exit_status;
|
return m_exit_status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "SoftCPU.h"
|
#include "SoftCPU.h"
|
||||||
#include "SoftMMU.h"
|
#include "SoftMMU.h"
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <LibELF/Loader.h>
|
||||||
#include <LibX86/Instruction.h>
|
#include <LibX86/Instruction.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
@ -38,7 +39,9 @@ class Emulator {
|
||||||
public:
|
public:
|
||||||
Emulator();
|
Emulator();
|
||||||
|
|
||||||
int exec(X86::SimpleInstructionStream&, u32 base);
|
bool load_elf(const ELF::Loader&);
|
||||||
|
|
||||||
|
int exec();
|
||||||
u32 virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3);
|
u32 virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3);
|
||||||
|
|
||||||
SoftMMU& mmu() { return m_mmu; }
|
SoftMMU& mmu() { return m_mmu; }
|
||||||
|
|
|
@ -78,9 +78,30 @@ void SoftCPU::dump() const
|
||||||
printf("o=%u s=%u z=%u a=%u p=%u c=%u\n", of(), sf(), zf(), af(), pf(), cf());
|
printf("o=%u s=%u z=%u a=%u p=%u c=%u\n", of(), sf(), zf(), af(), pf(), cf());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u8 SoftCPU::read8()
|
||||||
|
{
|
||||||
|
auto value = read_memory8({ cs(), eip() });
|
||||||
|
m_eip += 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 SoftCPU::read16()
|
||||||
|
{
|
||||||
|
auto value = read_memory16({ cs(), eip() });
|
||||||
|
m_eip += 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 SoftCPU::read32()
|
||||||
|
{
|
||||||
|
auto value = read_memory32({ cs(), eip() });
|
||||||
|
m_eip += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
u8 SoftCPU::read_memory8(X86::LogicalAddress address)
|
u8 SoftCPU::read_memory8(X86::LogicalAddress address)
|
||||||
{
|
{
|
||||||
ASSERT(address.selector() == 0x20);
|
ASSERT(address.selector() == 0x18 || address.selector() == 0x20);
|
||||||
auto value = m_emulator.mmu().read8(address.offset());
|
auto value = m_emulator.mmu().read8(address.offset());
|
||||||
printf("\033[36;1mread_memory8: @%08x -> %02x\033[0m\n", address.offset(), value);
|
printf("\033[36;1mread_memory8: @%08x -> %02x\033[0m\n", address.offset(), value);
|
||||||
return value;
|
return value;
|
||||||
|
@ -88,7 +109,7 @@ u8 SoftCPU::read_memory8(X86::LogicalAddress address)
|
||||||
|
|
||||||
u16 SoftCPU::read_memory16(X86::LogicalAddress address)
|
u16 SoftCPU::read_memory16(X86::LogicalAddress address)
|
||||||
{
|
{
|
||||||
ASSERT(address.selector() == 0x20);
|
ASSERT(address.selector() == 0x18 || address.selector() == 0x20);
|
||||||
auto value = m_emulator.mmu().read16(address.offset());
|
auto value = m_emulator.mmu().read16(address.offset());
|
||||||
printf("\033[36;1mread_memory16: @%08x -> %04x\033[0m\n", address.offset(), value);
|
printf("\033[36;1mread_memory16: @%08x -> %04x\033[0m\n", address.offset(), value);
|
||||||
return value;
|
return value;
|
||||||
|
@ -96,7 +117,7 @@ u16 SoftCPU::read_memory16(X86::LogicalAddress address)
|
||||||
|
|
||||||
u32 SoftCPU::read_memory32(X86::LogicalAddress address)
|
u32 SoftCPU::read_memory32(X86::LogicalAddress address)
|
||||||
{
|
{
|
||||||
ASSERT(address.selector() == 0x20);
|
ASSERT(address.selector() == 0x18 || address.selector() == 0x20);
|
||||||
auto value = m_emulator.mmu().read32(address.offset());
|
auto value = m_emulator.mmu().read32(address.offset());
|
||||||
printf("\033[36;1mread_memory32: @%08x -> %08x\033[0m\n", address.offset(), value);
|
printf("\033[36;1mread_memory32: @%08x -> %08x\033[0m\n", address.offset(), value);
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -48,11 +48,16 @@ union PartAddressableRegister {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
class SoftCPU final : public X86::Interpreter {
|
class SoftCPU final
|
||||||
|
: public X86::Interpreter
|
||||||
|
, public X86::InstructionStream {
|
||||||
public:
|
public:
|
||||||
explicit SoftCPU(Emulator&);
|
explicit SoftCPU(Emulator&);
|
||||||
void dump() const;
|
void dump() const;
|
||||||
|
|
||||||
|
u32 eip() const { return m_eip; }
|
||||||
|
void set_eip(u32 eip) { m_eip = eip; }
|
||||||
|
|
||||||
struct Flags {
|
struct Flags {
|
||||||
enum Flag {
|
enum Flag {
|
||||||
CF = 0x0001,
|
CF = 0x0001,
|
||||||
|
@ -274,6 +279,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// ^X86::InstructionStream
|
||||||
|
virtual bool can_read() override { return false; }
|
||||||
|
virtual u8 read8() override;
|
||||||
|
virtual u16 read16() override;
|
||||||
|
virtual u32 read32() override;
|
||||||
|
|
||||||
|
// ^X86::Interpreter
|
||||||
virtual void AAA(const X86::Instruction&) override;
|
virtual void AAA(const X86::Instruction&) override;
|
||||||
virtual void AAD(const X86::Instruction&) override;
|
virtual void AAD(const X86::Instruction&) override;
|
||||||
virtual void AAM(const X86::Instruction&) override;
|
virtual void AAM(const X86::Instruction&) override;
|
||||||
|
@ -779,6 +791,8 @@ private:
|
||||||
PartAddressableRegister m_gpr[8];
|
PartAddressableRegister m_gpr[8];
|
||||||
u16 m_segment[8] { 0 };
|
u16 m_segment[8] { 0 };
|
||||||
u32 m_eflags { 0 };
|
u32 m_eflags { 0 };
|
||||||
|
|
||||||
|
u32 m_eip { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include <AK/MappedFile.h>
|
#include <AK/MappedFile.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibELF/Loader.h>
|
#include <LibELF/Loader.h>
|
||||||
#include <LibX86/Instruction.h>
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
@ -48,15 +47,9 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
auto elf = ELF::Loader::create((const u8*)mapped_file.data(), mapped_file.size());
|
auto elf = ELF::Loader::create((const u8*)mapped_file.data(), mapped_file.size());
|
||||||
|
|
||||||
auto _start_symbol = elf->find_demangled_function("_start");
|
|
||||||
if (!_start_symbol.has_value()) {
|
|
||||||
warn() << "Could not find '_start' symbol in executable";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto main_code = _start_symbol.value().raw_data();
|
|
||||||
X86::SimpleInstructionStream stream((const u8*)main_code.characters_without_null_termination(), main_code.length());
|
|
||||||
|
|
||||||
UserspaceEmulator::Emulator emulator;
|
UserspaceEmulator::Emulator emulator;
|
||||||
return emulator.exec(stream, _start_symbol.value().value());
|
if (!emulator.load_elf(*elf))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return emulator.exec();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue