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.
This commit is contained in:
Nico Weber 2020-09-23 14:45:43 -04:00 committed by GitHub
parent 1fa5a526e8
commit f1c0f661f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
Notes: sideshowbarker 2024-07-19 02:16:09 +09:00
12 changed files with 156 additions and 0 deletions

View File

@ -116,6 +116,23 @@ ValueWithShadow<u32> MmapRegion::read32(u32 offset)
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
ValueWithShadow<u64> 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<const u64*>(m_data + offset), *reinterpret_cast<const u64*>(m_shadow_data + offset) };
}
void MmapRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
if (!is_writable()) {
@ -171,4 +188,23 @@ void MmapRegion::write32(u32 offset, ValueWithShadow<u32> value)
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::write64(u32 offset, ValueWithShadow<u64> 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<u64*>(m_data + offset) = value.value();
*reinterpret_cast<u64*>(m_shadow_data + offset) = value.shadow();
}
}

View File

@ -40,10 +40,12 @@ public:
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual ValueWithShadow<u64> read64(u32 offset) override;
virtual void write8(u32 offset, ValueWithShadow<u8>) override;
virtual void write16(u32 offset, ValueWithShadow<u16>) override;
virtual void write32(u32 offset, ValueWithShadow<u32>) override;
virtual void write64(u32 offset, ValueWithShadow<u64>) override;
u8* data() { return m_data; }
u8* shadow_data() { return m_shadow_data; }

View File

@ -70,6 +70,12 @@ ValueWithShadow<u32> SharedBufferRegion::read32(u32 offset)
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
ValueWithShadow<u64> SharedBufferRegion::read64(u32 offset)
{
ASSERT(offset + 7 < size());
return { *reinterpret_cast<const u64*>(m_data + offset), *reinterpret_cast<const u64*>(m_shadow_data + offset) };
}
void SharedBufferRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
ASSERT(offset < size());
@ -91,6 +97,13 @@ void SharedBufferRegion::write32(u32 offset, ValueWithShadow<u32> value)
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
void SharedBufferRegion::write64(u32 offset, ValueWithShadow<u64> value)
{
ASSERT(offset + 7 < size());
*reinterpret_cast<u64*>(m_data + offset) = value.value();
*reinterpret_cast<u64*>(m_shadow_data + offset) = value.shadow();
}
int SharedBufferRegion::allow_all()
{
return syscall(SC_shbuf_allow_all, m_shbuf_id);

View File

@ -39,10 +39,12 @@ public:
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual ValueWithShadow<u64> read64(u32 offset) override;
virtual void write8(u32 offset, ValueWithShadow<u8>) override;
virtual void write16(u32 offset, ValueWithShadow<u16>) override;
virtual void write32(u32 offset, ValueWithShadow<u32>) override;
virtual void write64(u32 offset, ValueWithShadow<u64>) override;
u8* data() { return m_data; }

View File

@ -61,6 +61,12 @@ ValueWithShadow<u32> SimpleRegion::read32(u32 offset)
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
ValueWithShadow<u64> SimpleRegion::read64(u32 offset)
{
ASSERT(offset + 7 < size());
return { *reinterpret_cast<const u64*>(m_data + offset), *reinterpret_cast<const u64*>(m_shadow_data + offset) };
}
void SimpleRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
ASSERT(offset < size());
@ -82,6 +88,13 @@ void SimpleRegion::write32(u32 offset, ValueWithShadow<u32> value)
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
void SimpleRegion::write64(u32 offset, ValueWithShadow<u64> value)
{
ASSERT(offset + 7 < size());
*reinterpret_cast<u64*>(m_data + offset) = value.value();
*reinterpret_cast<u64*>(m_shadow_data + offset) = value.shadow();
}
u8* SimpleRegion::cacheable_ptr(u32 offset)
{
return m_data + offset;

View File

@ -38,10 +38,12 @@ public:
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual ValueWithShadow<u64> read64(u32 offset) override;
virtual void write8(u32 offset, ValueWithShadow<u8>) override;
virtual void write16(u32 offset, ValueWithShadow<u16>) override;
virtual void write32(u32 offset, ValueWithShadow<u32>) override;
virtual void write64(u32 offset, ValueWithShadow<u64>) override;
u8* data() { return m_data; }
u8* shadow_data() { return m_shadow_data; }

View File

@ -158,6 +158,16 @@ ValueWithShadow<u32> SoftCPU::read_memory32(X86::LogicalAddress address)
return value;
}
ValueWithShadow<u64> 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<u8> value)
{
ASSERT(address.selector() == 0x20 || address.selector() == 0x28);
@ -185,6 +195,15 @@ void SoftCPU::write_memory32(X86::LogicalAddress address, ValueWithShadow<u32> v
m_emulator.mmu().write32(address, value);
}
void SoftCPU::write_memory64(X86::LogicalAddress address, ValueWithShadow<u64> 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);

View File

@ -360,6 +360,7 @@ public:
ValueWithShadow<u8> read_memory8(X86::LogicalAddress);
ValueWithShadow<u16> read_memory16(X86::LogicalAddress);
ValueWithShadow<u32> read_memory32(X86::LogicalAddress);
ValueWithShadow<u64> read_memory64(X86::LogicalAddress);
template<typename T>
ValueWithShadow<T> read_memory(X86::LogicalAddress address)
@ -375,6 +376,7 @@ public:
void write_memory8(X86::LogicalAddress, ValueWithShadow<u8>);
void write_memory16(X86::LogicalAddress, ValueWithShadow<u16>);
void write_memory32(X86::LogicalAddress, ValueWithShadow<u32>);
void write_memory64(X86::LogicalAddress, ValueWithShadow<u64>);
template<typename T>
void write_memory(X86::LogicalAddress address, ValueWithShadow<T> 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<const u64*>(m_cached_code_ptr);
m_cached_code_ptr += 8;
m_eip += 8;
return value;
}
}

View File

@ -97,6 +97,17 @@ ValueWithShadow<u32> SoftMMU::read32(X86::LogicalAddress address)
return region->read32(address.offset() - region->base());
}
ValueWithShadow<u64> 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<u8> value)
{
auto* region = find_region(address);
@ -130,6 +141,17 @@ void SoftMMU::write32(X86::LogicalAddress address, ValueWithShadow<u32> value)
region->write32(address.offset() - region->base(), value);
}
void SoftMMU::write64(X86::LogicalAddress address, ValueWithShadow<u64> 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.

View File

@ -52,10 +52,12 @@ public:
virtual void write8(u32 offset, ValueWithShadow<u8>) = 0;
virtual void write16(u32 offset, ValueWithShadow<u16>) = 0;
virtual void write32(u32 offset, ValueWithShadow<u32>) = 0;
virtual void write64(u32 offset, ValueWithShadow<u64>) = 0;
virtual ValueWithShadow<u8> read8(u32 offset) = 0;
virtual ValueWithShadow<u16> read16(u32 offset) = 0;
virtual ValueWithShadow<u32> read32(u32 offset) = 0;
virtual ValueWithShadow<u64> 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<u8> read8(X86::LogicalAddress);
ValueWithShadow<u16> read16(X86::LogicalAddress);
ValueWithShadow<u32> read32(X86::LogicalAddress);
ValueWithShadow<u64> read64(X86::LogicalAddress);
void write8(X86::LogicalAddress, ValueWithShadow<u8>);
void write16(X86::LogicalAddress, ValueWithShadow<u16>);
void write32(X86::LogicalAddress, ValueWithShadow<u32>);
void write64(X86::LogicalAddress, ValueWithShadow<u64>);
Region* find_region(X86::LogicalAddress);

View File

@ -111,6 +111,8 @@ private:
template<typename T>
ALWAYS_INLINE ValueWithShadow<T> 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)

View File

@ -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<typename CPU, typename T>
void write32(CPU&, const Instruction&, T);
template<typename CPU, typename T>
void write64(CPU&, const Instruction&, T);
template<typename T, typename CPU>
T read8(CPU&, const Instruction&);
@ -392,6 +401,8 @@ public:
T read16(CPU&, const Instruction&);
template<typename T, typename CPU>
T read32(CPU&, const Instruction&);
template<typename T, typename CPU>
T read64(CPU&, const Instruction&);
template<typename CPU>
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<typename CPU, typename T>
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<typename T, typename CPU>
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<typename T, typename CPU>
ALWAYS_INLINE T MemoryOrRegisterReference::read64(CPU& cpu, const Instruction& insn)
{
ASSERT(!is_register());
auto address = resolve(cpu, insn);
return cpu.read_memory64(address);
}
template<typename InstructionStreamType>
ALWAYS_INLINE Instruction Instruction::from_stream(InstructionStreamType& stream, bool o32, bool a32)
{