From f1c0f661f44b54894aee0efb5c76f32af33430d6 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 23 Sep 2020 14:45:43 -0400 Subject: [PATCH] UserspaceEmulator+LibX86: Add support for 64-bit memory reads and writes (#3584) This is useful for reading and writing doubles for #3329. It is also useful for emulating 64-bit binaries. MemoryOrRegisterReference assumes that 64-bit values are always memory references since that's enough for fpu support. If we ever want to emulate 64-bit binaries, that part will need minor updating. --- DevTools/UserspaceEmulator/MmapRegion.cpp | 36 +++++++++++++++++++ DevTools/UserspaceEmulator/MmapRegion.h | 2 ++ .../UserspaceEmulator/SharedBufferRegion.cpp | 13 +++++++ .../UserspaceEmulator/SharedBufferRegion.h | 2 ++ DevTools/UserspaceEmulator/SimpleRegion.cpp | 13 +++++++ DevTools/UserspaceEmulator/SimpleRegion.h | 2 ++ DevTools/UserspaceEmulator/SoftCPU.cpp | 19 ++++++++++ DevTools/UserspaceEmulator/SoftCPU.h | 14 ++++++++ DevTools/UserspaceEmulator/SoftMMU.cpp | 22 ++++++++++++ DevTools/UserspaceEmulator/SoftMMU.h | 4 +++ DevTools/UserspaceEmulator/ValueWithShadow.h | 2 ++ Libraries/LibX86/Instruction.h | 27 ++++++++++++++ 12 files changed, 156 insertions(+) diff --git a/DevTools/UserspaceEmulator/MmapRegion.cpp b/DevTools/UserspaceEmulator/MmapRegion.cpp index e507461c853..ca1b87789f6 100644 --- a/DevTools/UserspaceEmulator/MmapRegion.cpp +++ b/DevTools/UserspaceEmulator/MmapRegion.cpp @@ -116,6 +116,23 @@ ValueWithShadow MmapRegion::read32(u32 offset) return { *reinterpret_cast(m_data + offset), *reinterpret_cast(m_shadow_data + offset) }; } +ValueWithShadow MmapRegion::read64(u32 offset) +{ + if (!is_readable()) { + warn() << "64-bit read from unreadable MmapRegion @ " << (const void*)(base() + offset); + Emulator::the().dump_backtrace(); + TODO(); + } + + if (is_malloc_block()) { + if (auto* tracer = Emulator::the().malloc_tracer()) + tracer->audit_read(base() + offset, 8); + } + + ASSERT(offset + 7 < size()); + return { *reinterpret_cast(m_data + offset), *reinterpret_cast(m_shadow_data + offset) }; +} + void MmapRegion::write8(u32 offset, ValueWithShadow value) { if (!is_writable()) { @@ -171,4 +188,23 @@ void MmapRegion::write32(u32 offset, ValueWithShadow value) *reinterpret_cast(m_shadow_data + offset) = value.shadow(); } +void MmapRegion::write64(u32 offset, ValueWithShadow value) +{ + if (!is_writable()) { + warn() << "64-bit write to unreadable MmapRegion @ " << (const void*)(base() + offset); + Emulator::the().dump_backtrace(); + TODO(); + } + + if (is_malloc_block()) { + if (auto* tracer = Emulator::the().malloc_tracer()) + tracer->audit_write(base() + offset, 8); + } + + ASSERT(offset + 7 < size()); + ASSERT(m_data != m_shadow_data); + *reinterpret_cast(m_data + offset) = value.value(); + *reinterpret_cast(m_shadow_data + offset) = value.shadow(); +} + } diff --git a/DevTools/UserspaceEmulator/MmapRegion.h b/DevTools/UserspaceEmulator/MmapRegion.h index 8c088f7f8bd..882763cbde1 100644 --- a/DevTools/UserspaceEmulator/MmapRegion.h +++ b/DevTools/UserspaceEmulator/MmapRegion.h @@ -40,10 +40,12 @@ public: virtual ValueWithShadow read8(u32 offset) override; virtual ValueWithShadow read16(u32 offset) override; virtual ValueWithShadow read32(u32 offset) override; + virtual ValueWithShadow read64(u32 offset) override; virtual void write8(u32 offset, ValueWithShadow) override; virtual void write16(u32 offset, ValueWithShadow) override; virtual void write32(u32 offset, ValueWithShadow) override; + virtual void write64(u32 offset, ValueWithShadow) override; u8* data() { return m_data; } u8* shadow_data() { return m_shadow_data; } diff --git a/DevTools/UserspaceEmulator/SharedBufferRegion.cpp b/DevTools/UserspaceEmulator/SharedBufferRegion.cpp index 02c4cfbb8f8..aec26c2082c 100644 --- a/DevTools/UserspaceEmulator/SharedBufferRegion.cpp +++ b/DevTools/UserspaceEmulator/SharedBufferRegion.cpp @@ -70,6 +70,12 @@ ValueWithShadow SharedBufferRegion::read32(u32 offset) return { *reinterpret_cast(m_data + offset), *reinterpret_cast(m_shadow_data + offset) }; } +ValueWithShadow SharedBufferRegion::read64(u32 offset) +{ + ASSERT(offset + 7 < size()); + return { *reinterpret_cast(m_data + offset), *reinterpret_cast(m_shadow_data + offset) }; +} + void SharedBufferRegion::write8(u32 offset, ValueWithShadow value) { ASSERT(offset < size()); @@ -91,6 +97,13 @@ void SharedBufferRegion::write32(u32 offset, ValueWithShadow value) *reinterpret_cast(m_shadow_data + offset) = value.shadow(); } +void SharedBufferRegion::write64(u32 offset, ValueWithShadow value) +{ + ASSERT(offset + 7 < size()); + *reinterpret_cast(m_data + offset) = value.value(); + *reinterpret_cast(m_shadow_data + offset) = value.shadow(); +} + int SharedBufferRegion::allow_all() { return syscall(SC_shbuf_allow_all, m_shbuf_id); diff --git a/DevTools/UserspaceEmulator/SharedBufferRegion.h b/DevTools/UserspaceEmulator/SharedBufferRegion.h index 590f83c1d89..80c76e1ebc3 100644 --- a/DevTools/UserspaceEmulator/SharedBufferRegion.h +++ b/DevTools/UserspaceEmulator/SharedBufferRegion.h @@ -39,10 +39,12 @@ public: virtual ValueWithShadow read8(u32 offset) override; virtual ValueWithShadow read16(u32 offset) override; virtual ValueWithShadow read32(u32 offset) override; + virtual ValueWithShadow read64(u32 offset) override; virtual void write8(u32 offset, ValueWithShadow) override; virtual void write16(u32 offset, ValueWithShadow) override; virtual void write32(u32 offset, ValueWithShadow) override; + virtual void write64(u32 offset, ValueWithShadow) override; u8* data() { return m_data; } diff --git a/DevTools/UserspaceEmulator/SimpleRegion.cpp b/DevTools/UserspaceEmulator/SimpleRegion.cpp index cf5d2ee88c3..173efd0e329 100644 --- a/DevTools/UserspaceEmulator/SimpleRegion.cpp +++ b/DevTools/UserspaceEmulator/SimpleRegion.cpp @@ -61,6 +61,12 @@ ValueWithShadow SimpleRegion::read32(u32 offset) return { *reinterpret_cast(m_data + offset), *reinterpret_cast(m_shadow_data + offset) }; } +ValueWithShadow SimpleRegion::read64(u32 offset) +{ + ASSERT(offset + 7 < size()); + return { *reinterpret_cast(m_data + offset), *reinterpret_cast(m_shadow_data + offset) }; +} + void SimpleRegion::write8(u32 offset, ValueWithShadow value) { ASSERT(offset < size()); @@ -82,6 +88,13 @@ void SimpleRegion::write32(u32 offset, ValueWithShadow value) *reinterpret_cast(m_shadow_data + offset) = value.shadow(); } +void SimpleRegion::write64(u32 offset, ValueWithShadow value) +{ + ASSERT(offset + 7 < size()); + *reinterpret_cast(m_data + offset) = value.value(); + *reinterpret_cast(m_shadow_data + offset) = value.shadow(); +} + u8* SimpleRegion::cacheable_ptr(u32 offset) { return m_data + offset; diff --git a/DevTools/UserspaceEmulator/SimpleRegion.h b/DevTools/UserspaceEmulator/SimpleRegion.h index 85055cbe73a..4a65593041c 100644 --- a/DevTools/UserspaceEmulator/SimpleRegion.h +++ b/DevTools/UserspaceEmulator/SimpleRegion.h @@ -38,10 +38,12 @@ public: virtual ValueWithShadow read8(u32 offset) override; virtual ValueWithShadow read16(u32 offset) override; virtual ValueWithShadow read32(u32 offset) override; + virtual ValueWithShadow read64(u32 offset) override; virtual void write8(u32 offset, ValueWithShadow) override; virtual void write16(u32 offset, ValueWithShadow) override; virtual void write32(u32 offset, ValueWithShadow) override; + virtual void write64(u32 offset, ValueWithShadow) override; u8* data() { return m_data; } u8* shadow_data() { return m_shadow_data; } diff --git a/DevTools/UserspaceEmulator/SoftCPU.cpp b/DevTools/UserspaceEmulator/SoftCPU.cpp index 19d63cfe283..4e2ec7a5afc 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.cpp +++ b/DevTools/UserspaceEmulator/SoftCPU.cpp @@ -158,6 +158,16 @@ ValueWithShadow SoftCPU::read_memory32(X86::LogicalAddress address) return value; } +ValueWithShadow SoftCPU::read_memory64(X86::LogicalAddress address) +{ + ASSERT(address.selector() == 0x18 || address.selector() == 0x20 || address.selector() == 0x28); + auto value = m_emulator.mmu().read64(address); +#ifdef MEMORY_DEBUG + printf("\033[36;1mread_memory64: @%04x:%08x -> %016llx (%016llx)\033[0m\n", address.selector(), address.offset(), value.value(), value.shadow()); +#endif + return value; +} + void SoftCPU::write_memory8(X86::LogicalAddress address, ValueWithShadow value) { ASSERT(address.selector() == 0x20 || address.selector() == 0x28); @@ -185,6 +195,15 @@ void SoftCPU::write_memory32(X86::LogicalAddress address, ValueWithShadow v m_emulator.mmu().write32(address, value); } +void SoftCPU::write_memory64(X86::LogicalAddress address, ValueWithShadow value) +{ + ASSERT(address.selector() == 0x20 || address.selector() == 0x28); +#ifdef MEMORY_DEBUG + printf("\033[35;1mwrite_memory64: @%04x:%08x <- %016llx (%016llx)\033[0m\n", address.selector(), address.offset(), value.value(), value.shadow()); +#endif + m_emulator.mmu().write64(address, value); +} + void SoftCPU::push_string(const StringView& string) { size_t space_to_allocate = round_up_to_power_of_two(string.length() + 1, 16); diff --git a/DevTools/UserspaceEmulator/SoftCPU.h b/DevTools/UserspaceEmulator/SoftCPU.h index 43a0a2eaba6..66a6ff8c7c6 100644 --- a/DevTools/UserspaceEmulator/SoftCPU.h +++ b/DevTools/UserspaceEmulator/SoftCPU.h @@ -360,6 +360,7 @@ public: ValueWithShadow read_memory8(X86::LogicalAddress); ValueWithShadow read_memory16(X86::LogicalAddress); ValueWithShadow read_memory32(X86::LogicalAddress); + ValueWithShadow read_memory64(X86::LogicalAddress); template ValueWithShadow read_memory(X86::LogicalAddress address) @@ -375,6 +376,7 @@ public: void write_memory8(X86::LogicalAddress, ValueWithShadow); void write_memory16(X86::LogicalAddress, ValueWithShadow); void write_memory32(X86::LogicalAddress, ValueWithShadow); + void write_memory64(X86::LogicalAddress, ValueWithShadow); template void write_memory(X86::LogicalAddress address, ValueWithShadow data) @@ -456,6 +458,7 @@ public: virtual u8 read8() override; virtual u16 read16() override; virtual u32 read32() override; + virtual u64 read64() override; private: // ^X86::Interpreter @@ -1154,4 +1157,15 @@ ALWAYS_INLINE u32 SoftCPU::read32() return value; } +ALWAYS_INLINE u64 SoftCPU::read64() +{ + if (!m_cached_code_ptr || (m_cached_code_ptr + 8) >= m_cached_code_end) + update_code_cache(); + + u64 value = *reinterpret_cast(m_cached_code_ptr); + m_cached_code_ptr += 8; + m_eip += 8; + return value; +} + } diff --git a/DevTools/UserspaceEmulator/SoftMMU.cpp b/DevTools/UserspaceEmulator/SoftMMU.cpp index 5c11b3ea70e..0d674f22b1a 100644 --- a/DevTools/UserspaceEmulator/SoftMMU.cpp +++ b/DevTools/UserspaceEmulator/SoftMMU.cpp @@ -97,6 +97,17 @@ ValueWithShadow SoftMMU::read32(X86::LogicalAddress address) return region->read32(address.offset() - region->base()); } +ValueWithShadow SoftMMU::read64(X86::LogicalAddress address) +{ + auto* region = find_region(address); + if (!region) { + warn() << "SoftMMU::read64: No region for @" << (const void*)address.offset(); + TODO(); + } + + return region->read64(address.offset() - region->base()); +} + void SoftMMU::write8(X86::LogicalAddress address, ValueWithShadow value) { auto* region = find_region(address); @@ -130,6 +141,17 @@ void SoftMMU::write32(X86::LogicalAddress address, ValueWithShadow value) region->write32(address.offset() - region->base(), value); } +void SoftMMU::write64(X86::LogicalAddress address, ValueWithShadow value) +{ + auto* region = find_region(address); + if (!region) { + warn() << "SoftMMU::write64: No region for @" << (const void*)address.offset(); + TODO(); + } + + region->write64(address.offset() - region->base(), value); +} + void SoftMMU::copy_to_vm(FlatPtr destination, const void* source, size_t size) { // FIXME: We should have a way to preserve the shadow data here as well. diff --git a/DevTools/UserspaceEmulator/SoftMMU.h b/DevTools/UserspaceEmulator/SoftMMU.h index 854ab97de7f..5318bf6b5df 100644 --- a/DevTools/UserspaceEmulator/SoftMMU.h +++ b/DevTools/UserspaceEmulator/SoftMMU.h @@ -52,10 +52,12 @@ public: virtual void write8(u32 offset, ValueWithShadow) = 0; virtual void write16(u32 offset, ValueWithShadow) = 0; virtual void write32(u32 offset, ValueWithShadow) = 0; + virtual void write64(u32 offset, ValueWithShadow) = 0; virtual ValueWithShadow read8(u32 offset) = 0; virtual ValueWithShadow read16(u32 offset) = 0; virtual ValueWithShadow read32(u32 offset) = 0; + virtual ValueWithShadow read64(u32 offset) = 0; virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; } virtual bool is_shared_buffer() const { return false; } @@ -85,10 +87,12 @@ public: ValueWithShadow read8(X86::LogicalAddress); ValueWithShadow read16(X86::LogicalAddress); ValueWithShadow read32(X86::LogicalAddress); + ValueWithShadow read64(X86::LogicalAddress); void write8(X86::LogicalAddress, ValueWithShadow); void write16(X86::LogicalAddress, ValueWithShadow); void write32(X86::LogicalAddress, ValueWithShadow); + void write64(X86::LogicalAddress, ValueWithShadow); Region* find_region(X86::LogicalAddress); diff --git a/DevTools/UserspaceEmulator/ValueWithShadow.h b/DevTools/UserspaceEmulator/ValueWithShadow.h index a0bb3e1b7c7..de66248a3d1 100644 --- a/DevTools/UserspaceEmulator/ValueWithShadow.h +++ b/DevTools/UserspaceEmulator/ValueWithShadow.h @@ -111,6 +111,8 @@ private: template ALWAYS_INLINE ValueWithShadow shadow_wrap_as_initialized(T value) { + if constexpr (sizeof(T) == 8) + return { value, 0x01010101'01010101LLU }; if constexpr (sizeof(T) == 4) return { value, 0x01010101 }; if constexpr (sizeof(T) == 2) diff --git a/Libraries/LibX86/Instruction.h b/Libraries/LibX86/Instruction.h index 08cb5f38f49..9edf833d48e 100644 --- a/Libraries/LibX86/Instruction.h +++ b/Libraries/LibX86/Instruction.h @@ -315,6 +315,7 @@ public: virtual u8 read8() = 0; virtual u16 read16() = 0; virtual u32 read32() = 0; + virtual u64 read64() = 0; }; class SimpleInstructionStream final : public InstructionStream { @@ -347,6 +348,12 @@ public: return ((u32)msw << 16) | (u32)lsw; } + virtual u64 read64() override + { + u32 lsw = read32(); + u32 msw = read32(); + return ((u64)msw << 32) | (u64)lsw; + } size_t offset() const { return m_offset; } private: @@ -385,6 +392,8 @@ public: void write16(CPU&, const Instruction&, T); template void write32(CPU&, const Instruction&, T); + template + void write64(CPU&, const Instruction&, T); template T read8(CPU&, const Instruction&); @@ -392,6 +401,8 @@ public: T read16(CPU&, const Instruction&); template T read32(CPU&, const Instruction&); + template + T read64(CPU&, const Instruction&); template LogicalAddress resolve(const CPU&, const Instruction&); @@ -752,6 +763,14 @@ ALWAYS_INLINE void MemoryOrRegisterReference::write32(CPU& cpu, const Instructio cpu.write_memory32(address, value); } +template +ALWAYS_INLINE void MemoryOrRegisterReference::write64(CPU& cpu, const Instruction& insn, T value) +{ + ASSERT(!is_register()); + auto address = resolve(cpu, insn); + cpu.write_memory64(address, value); +} + template ALWAYS_INLINE T MemoryOrRegisterReference::read8(CPU& cpu, const Instruction& insn) { @@ -782,6 +801,14 @@ ALWAYS_INLINE T MemoryOrRegisterReference::read32(CPU& cpu, const Instruction& i return cpu.read_memory32(address); } +template +ALWAYS_INLINE T MemoryOrRegisterReference::read64(CPU& cpu, const Instruction& insn) +{ + ASSERT(!is_register()); + auto address = resolve(cpu, insn); + return cpu.read_memory64(address); +} + template ALWAYS_INLINE Instruction Instruction::from_stream(InstructionStreamType& stream, bool o32, bool a32) {