mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:02:45 +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
	
	 Andreas Kling
						Andreas Kling