From 734f63d522e8cefc9c2f919f90dab733a0e171d7 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 12 Jul 2020 00:54:09 +0200 Subject: [PATCH] UserspaceEmulator: Add basic TLS (thread-local storage) support The SoftMMU now receives full X86::LogicalAddress values from SoftCPU. This allows the MMU to reroute TLS accesses to a special memory region. The ELF executable's PT_TLS header tells us how to allocate the TLS. Basically, the GS register points to a magical 4-byte area which has a pointer to the TCB (thread control block). The TCB lives in normal flat memory space and is accessed through the DS register. --- DevTools/UserspaceEmulator/Emulator.cpp | 20 ++++++++-- DevTools/UserspaceEmulator/SoftCPU.cpp | 37 +++++++++--------- DevTools/UserspaceEmulator/SoftMMU.cpp | 51 +++++++++++++++---------- DevTools/UserspaceEmulator/SoftMMU.h | 19 +++++---- 4 files changed, 77 insertions(+), 50 deletions(-) diff --git a/DevTools/UserspaceEmulator/Emulator.cpp b/DevTools/UserspaceEmulator/Emulator.cpp index 0b0654143e..1968af7e50 100644 --- a/DevTools/UserspaceEmulator/Emulator.cpp +++ b/DevTools/UserspaceEmulator/Emulator.cpp @@ -124,11 +124,23 @@ void Emulator::setup_stack() bool Emulator::load_elf() { m_elf->image().for_each_program_header([&](const ELF::Image::ProgramHeader& program_header) { - if (program_header.type() != PT_LOAD) + if (program_header.type() == PT_LOAD) { + auto region = make(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)); return; - auto region = make(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)); + } + if (program_header.type() == PT_TLS) { + auto tcb_region = make(0x20000000, program_header.size_in_memory()); + memcpy(tcb_region->data(), program_header.raw_data(), program_header.size_in_image()); + + auto tls_region = make(0, 4); + tls_region->write32(0, tcb_region->base() + 8); + + mmu().add_region(move(tcb_region)); + mmu().set_tls_region(move(tls_region)); + return; + } }); m_cpu.set_eip(m_elf->image().entry().get()); diff --git a/DevTools/UserspaceEmulator/SoftCPU.cpp b/DevTools/UserspaceEmulator/SoftCPU.cpp index 1547b26239..b182d98efa 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.cpp +++ b/DevTools/UserspaceEmulator/SoftCPU.cpp @@ -51,6 +51,7 @@ SoftCPU::SoftCPU(Emulator& emulator) m_segment[(int)X86::SegmentRegister::DS] = 0x20; m_segment[(int)X86::SegmentRegister::ES] = 0x20; m_segment[(int)X86::SegmentRegister::SS] = 0x20; + m_segment[(int)X86::SegmentRegister::GS] = 0x28; } void SoftCPU::dump() const @@ -83,59 +84,59 @@ u32 SoftCPU::read32() u8 SoftCPU::read_memory8(X86::LogicalAddress address) { - ASSERT(address.selector() == 0x18 || address.selector() == 0x20); - auto value = m_emulator.mmu().read8(address.offset()); + ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28); + auto value = m_emulator.mmu().read8(address); #ifdef MEMORY_DEBUG - printf("\033[36;1mread_memory8: @%08x -> %02x\033[0m\n", address.offset(), value); + printf("\033[36;1mread_memory8: @%08x:%08x -> %02x\033[0m\n", address.selector(), address.offset(), value); #endif return value; } u16 SoftCPU::read_memory16(X86::LogicalAddress address) { - ASSERT(address.selector() == 0x18 || address.selector() == 0x20); - auto value = m_emulator.mmu().read16(address.offset()); + ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28); + auto value = m_emulator.mmu().read16(address); #ifdef MEMORY_DEBUG - printf("\033[36;1mread_memory16: @%08x -> %04x\033[0m\n", address.offset(), value); + printf("\033[36;1mread_memory16: @%04x:%08x -> %04x\033[0m\n", address.selector(), address.offset(), value); #endif return value; } u32 SoftCPU::read_memory32(X86::LogicalAddress address) { - ASSERT(address.selector() == 0x18 || address.selector() == 0x20); - auto value = m_emulator.mmu().read32(address.offset()); + ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28); + auto value = m_emulator.mmu().read32(address); #ifdef MEMORY_DEBUG - printf("\033[36;1mread_memory32: @%08x -> %08x\033[0m\n", address.offset(), value); + printf("\033[36;1mread_memory32: @%04x:%08x -> %08x\033[0m\n", address.selector(), address.offset(), value); #endif return value; } void SoftCPU::write_memory8(X86::LogicalAddress address, u8 value) { - ASSERT(address.selector() == 0x20); + ASSERT(address.selector() == 0x20 || address.selector() == 0x28); #ifdef MEMORY_DEBUG - printf("\033[35;1mwrite_memory8: @%08x <- %02x\033[0m\n", address.offset(), value); + printf("\033[35;1mwrite_memory8: @%04x:%08x <- %02x\033[0m\n", address.selector(), address.offset(), value); #endif - m_emulator.mmu().write8(address.offset(), value); + m_emulator.mmu().write8(address, value); } void SoftCPU::write_memory16(X86::LogicalAddress address, u16 value) { - ASSERT(address.selector() == 0x20); + ASSERT(address.selector() == 0x20 || address.selector() == 0x28); #ifdef MEMORY_DEBUG - printf("\033[35;1mwrite_memory16: @%08x <- %04x\033[0m\n", address.offset(), value); + printf("\033[35;1mwrite_memory16: @%04x:%08x <- %04x\033[0m\n", address.selector(), address.offset(), value); #endif - m_emulator.mmu().write16(address.offset(), value); + m_emulator.mmu().write16(address, value); } void SoftCPU::write_memory32(X86::LogicalAddress address, u32 value) { - ASSERT(address.selector() == 0x20); + ASSERT(address.selector() == 0x20 || address.selector() == 0x28); #ifdef MEMORY_DEBUG - printf("\033[35;1mwrite_memory32: @%08x <- %08x\033[0m\n", address.offset(), value); + printf("\033[35;1mwrite_memory32: @%04x:%08x <- %08x\033[0m\n", address.selector(), address.offset(), value); #endif - m_emulator.mmu().write32(address.offset(), value); + m_emulator.mmu().write32(address, value); } void SoftCPU::push32(u32 value) diff --git a/DevTools/UserspaceEmulator/SoftMMU.cpp b/DevTools/UserspaceEmulator/SoftMMU.cpp index cc3174a3e2..cf4d95cf25 100644 --- a/DevTools/UserspaceEmulator/SoftMMU.cpp +++ b/DevTools/UserspaceEmulator/SoftMMU.cpp @@ -28,10 +28,13 @@ namespace UserspaceEmulator { -SoftMMU::Region* SoftMMU::find_region(u32 address) +SoftMMU::Region* SoftMMU::find_region(X86::LogicalAddress address) { + if (address.selector() == 0x28) + return m_tls_region.ptr(); + for (auto& region : m_regions) { - if (region.contains(address)) + if (region.contains(address.offset())) return ®ion; } return nullptr; @@ -39,75 +42,81 @@ SoftMMU::Region* SoftMMU::find_region(u32 address) void SoftMMU::add_region(NonnullOwnPtr region) { - ASSERT(!find_region(region->base())); + ASSERT(!find_region({ 0x20, region->base() })); // FIXME: More sanity checks pls m_regions.append(move(region)); } -u8 SoftMMU::read8(u32 address) +void SoftMMU::set_tls_region(NonnullOwnPtr region) +{ + ASSERT(!m_tls_region); + m_tls_region = move(region); +} + +u8 SoftMMU::read8(X86::LogicalAddress address) { auto* region = find_region(address); if (!region) { - warn() << "SoftMMU::read8: No region for @" << (const void*)address; + warn() << "SoftMMU::read8: No region for @" << (const void*)address.offset(); TODO(); } - return region->read8(address - region->base()); + return region->read8(address.offset() - region->base()); } -u16 SoftMMU::read16(u32 address) +u16 SoftMMU::read16(X86::LogicalAddress address) { auto* region = find_region(address); if (!region) { - warn() << "SoftMMU::read16: No region for @" << (const void*)address; + warn() << "SoftMMU::read16: No region for @" << (const void*)address.offset(); TODO(); } - return region->read16(address - region->base()); + return region->read16(address.offset() - region->base()); } -u32 SoftMMU::read32(u32 address) +u32 SoftMMU::read32(X86::LogicalAddress address) { auto* region = find_region(address); if (!region) { - warn() << "SoftMMU::read32: No region for @" << (const void*)address; + warn() << "SoftMMU::read32: No region for @" << (const void*)address.offset(); TODO(); } - return region->read32(address - region->base()); + return region->read32(address.offset() - region->base()); } -void SoftMMU::write8(u32 address, u8 value) +void SoftMMU::write8(X86::LogicalAddress address, u8 value) { auto* region = find_region(address); if (!region) { - warn() << "SoftMMU::write8: No region for @" << (const void*)address; + warn() << "SoftMMU::write8: No region for @" << (const void*)address.offset(); TODO(); } - region->write8(address - region->base(), value); + region->write8(address.offset() - region->base(), value); } -void SoftMMU::write16(u32 address, u16 value) +void SoftMMU::write16(X86::LogicalAddress address, u16 value) { auto* region = find_region(address); if (!region) { - warn() << "SoftMMU::write16: No region for @" << (const void*)address; + warn() << "SoftMMU::write16: No region for @" << (const void*)address.offset(); TODO(); } - region->write16(address - region->base(), value); + region->write16(address.offset() - region->base(), value); } -void SoftMMU::write32(u32 address, u32 value) +void SoftMMU::write32(X86::LogicalAddress address, u32 value) { auto* region = find_region(address); if (!region) { - warn() << "SoftMMU::write32: No region for @" << (const void*)address; + warn() << "SoftMMU::write32: No region for @" << (const void*)address.offset(); TODO(); } - region->write32(address - region->base(), value); + region->write32(address.offset() - region->base(), value); } } diff --git a/DevTools/UserspaceEmulator/SoftMMU.h b/DevTools/UserspaceEmulator/SoftMMU.h index fd12534a45..1a0f46c17c 100644 --- a/DevTools/UserspaceEmulator/SoftMMU.h +++ b/DevTools/UserspaceEmulator/SoftMMU.h @@ -27,7 +27,9 @@ #pragma once #include +#include #include +#include namespace UserspaceEmulator { @@ -63,18 +65,21 @@ public: u32 m_size { 0 }; }; - u8 read8(u32 address); - u16 read16(u32 address); - u32 read32(u32 address); + u8 read8(X86::LogicalAddress); + u16 read16(X86::LogicalAddress); + u32 read32(X86::LogicalAddress); - void write8(u32 address, u8 value); - void write16(u32 address, u16 value); - void write32(u32 address, u32 value); + void write8(X86::LogicalAddress, u8); + void write16(X86::LogicalAddress, u16); + void write32(X86::LogicalAddress, u32); + + Region* find_region(X86::LogicalAddress); - Region* find_region(u32 address); void add_region(NonnullOwnPtr); + void set_tls_region(NonnullOwnPtr); private: + OwnPtr m_tls_region; NonnullOwnPtrVector m_regions; };