UserspaceEmulator+LibX86: Start tracking uninitialized memory :^)

This patch introduces the concept of shadow bits. For every byte of
memory there is a corresponding shadow byte that contains metadata
about that memory.

Initially, the only metadata is whether the byte has been initialized
or not. That's represented by the least significant shadow bit.

Shadow bits travel together with regular values throughout the entire
CPU and MMU emulation. There are two main helper classes to facilitate
this: ValueWithShadow and ValueAndShadowReference.

ValueWithShadow<T> is basically a struct { T value; T shadow; } whereas
ValueAndShadowReference<T> is struct { T& value; T& shadow; }.

The latter is used as a wrapper around general-purpose registers, since
they can't use the plain ValueWithShadow memory as we need to be able
to address individual 8-bit and 16-bit subregisters (EAX, AX, AL, AH.)

Whenever a computation is made using uninitialized inputs, the result
is tainted and becomes uninitialized as well. This allows us to track
this state as it propagates throughout memory and registers.

This patch doesn't yet keep track of tainted flags, that will be an
important upcoming improvement to this.

I'm sure I've messed up some things here and there, but it seems to
basically work, so we have a place to start! :^)
This commit is contained in:
Andreas Kling 2020-07-21 02:29:59 +02:00
parent f2d3cc7325
commit be5f42adea
14 changed files with 1088 additions and 752 deletions

View file

@ -77,30 +77,30 @@ void Emulator::setup_stack(const Vector<String>& arguments)
auto stack_region = make<SimpleRegion>(stack_location, stack_size);
stack_region->set_stack(true);
m_mmu.add_region(move(stack_region));
m_cpu.set_esp(stack_location + stack_size);
m_cpu.set_esp(shadow_wrap_as_initialized<u32>(stack_location + stack_size));
Vector<u32> argv_entries;
for (auto& argument : arguments) {
m_cpu.push_string(argument.characters());
argv_entries.append(m_cpu.esp());
argv_entries.append(m_cpu.esp().value());
}
m_cpu.push32(0); // char** envp = { nullptr }
u32 envp = m_cpu.esp();
m_cpu.push32(shadow_wrap_as_initialized<u32>(0)); // char** envp = { nullptr }
u32 envp = m_cpu.esp().value();
m_cpu.push32(0); // char** argv = { argv_entries..., nullptr }
m_cpu.push32(shadow_wrap_as_initialized<u32>(0)); // char** argv = { argv_entries..., nullptr }
for (ssize_t i = argv_entries.size() - 1; i >= 0; --i)
m_cpu.push32(argv_entries[i]);
u32 argv = m_cpu.esp();
m_cpu.push32(shadow_wrap_as_initialized(argv_entries[i]));
u32 argv = m_cpu.esp().value();
m_cpu.push32(0); // (alignment)
m_cpu.push32(shadow_wrap_as_initialized<u32>(0)); // (alignment)
u32 argc = argv_entries.size();
m_cpu.push32(envp);
m_cpu.push32(argv);
m_cpu.push32(argc);
m_cpu.push32(0); // (alignment)
m_cpu.push32(shadow_wrap_as_initialized(envp));
m_cpu.push32(shadow_wrap_as_initialized(argv));
m_cpu.push32(shadow_wrap_as_initialized(argc));
m_cpu.push32(shadow_wrap_as_initialized<u32>(0)); // (alignment)
}
bool Emulator::load_elf()
@ -111,15 +111,18 @@ bool Emulator::load_elf()
if (program_header.is_executable() && !program_header.is_writable())
region->set_text(true);
memcpy(region->data(), program_header.raw_data(), program_header.size_in_image());
memset(region->shadow_data(), 0x01, program_header.size_in_memory());
mmu().add_region(move(region));
return;
}
if (program_header.type() == PT_TLS) {
auto tcb_region = make<SimpleRegion>(0x20000000, program_header.size_in_memory());
memcpy(tcb_region->data(), program_header.raw_data(), program_header.size_in_image());
memset(tcb_region->shadow_data(), 0x01, program_header.size_in_image());
auto tls_region = make<SimpleRegion>(0, 4);
tls_region->write32(0, tcb_region->base() + 8);
tls_region->write32(0, shadow_wrap_as_initialized(tcb_region->base() + 8));
memset(tls_region->shadow_data(), 0x01, 4);
mmu().add_region(move(tcb_region));
mmu().set_tls_region(move(tls_region));
@ -195,13 +198,15 @@ Vector<FlatPtr> Emulator::raw_backtrace()
Vector<FlatPtr> backtrace;
backtrace.append(m_cpu.eip());
u32 frame_ptr = m_cpu.ebp();
// FIXME: Maybe do something if the backtrace has uninitialized data in the frame chain.
u32 frame_ptr = m_cpu.ebp().value();
while (frame_ptr) {
u32 ret_ptr = m_mmu.read32({ 0x20, frame_ptr + 4 });
u32 ret_ptr = m_mmu.read32({ 0x20, frame_ptr + 4 }).value();
if (!ret_ptr)
break;
backtrace.append(ret_ptr);
frame_ptr = m_mmu.read32({ 0x20, frame_ptr });
frame_ptr = m_mmu.read32({ 0x20, frame_ptr }).value();
}
return backtrace;
}

View file

@ -28,6 +28,7 @@
#include "Emulator.h"
#include "MmapRegion.h"
#include <AK/LogStream.h>
#include <string.h>
//#define REACHABLE_DEBUG
@ -41,6 +42,13 @@ MallocTracer::MallocTracer()
void MallocTracer::target_did_malloc(Badge<SoftCPU>, FlatPtr address, size_t size)
{
auto* region = Emulator::the().mmu().find_region({ 0x20, address });
ASSERT(region);
ASSERT(region->is_mmap());
auto& mmap_region = static_cast<MmapRegion&>(*region);
auto* shadow_bits = mmap_region.shadow_data() + address - mmap_region.base();
memset(shadow_bits, 0, size);
if (auto* existing_mallocation = find_mallocation(address)) {
ASSERT(existing_mallocation->freed);
existing_mallocation->size = size;
@ -151,7 +159,7 @@ bool MallocTracer::is_reachable(const Mallocation& mallocation) const
size_t pointers_in_mallocation = other_mallocation.size / sizeof(u32);
for (size_t i = 0; i < pointers_in_mallocation; ++i) {
auto value = Emulator::the().mmu().read32({ 0x20, other_mallocation.address + i * sizeof(u32) });
if (value == mallocation.address) {
if (value.value() == mallocation.address && !value.is_uninitialized()) {
#ifdef REACHABLE_DEBUG
dbgprintf("mallocation %p is reachable from other mallocation %p\n", mallocation.address, other_mallocation.address);
#endif
@ -176,7 +184,7 @@ bool MallocTracer::is_reachable(const Mallocation& mallocation) const
size_t pointers_in_region = region.size() / sizeof(u32);
for (size_t i = 0; i < pointers_in_region; ++i) {
auto value = region.read32(i * sizeof(u32));
if (value == mallocation.address) {
if (value.value() == mallocation.address && !value.is_uninitialized()) {
#ifdef REACHABLE_DEBUG
dbgprintf("mallocation %p is reachable from region %p-%p\n", mallocation.address, region.base(), region.end() - 1);
#endif

View file

@ -51,10 +51,12 @@ MmapRegion::MmapRegion(u32 base, u32 size, int prot)
: Region(base, size)
, m_prot(prot)
{
m_shadow_data = (u8*)calloc(1, size);
}
MmapRegion::~MmapRegion()
{
free(m_shadow_data);
if (m_file_backed)
munmap(m_data, size());
else
@ -68,7 +70,7 @@ bool MmapRegion::is_malloc_block() const
return !m_file_backed;
}
u8 MmapRegion::read8(FlatPtr offset)
ValueWithShadow<u8> MmapRegion::read8(FlatPtr offset)
{
if (!is_readable()) {
warn() << "8-bit read from unreadable MmapRegion @ " << (const void*)(base() + offset);
@ -82,10 +84,10 @@ u8 MmapRegion::read8(FlatPtr offset)
}
ASSERT(offset < size());
return *reinterpret_cast<const u8*>(m_data + offset);
return { *reinterpret_cast<const u8*>(m_data + offset), *reinterpret_cast<const u8*>(m_shadow_data + offset) };
}
u16 MmapRegion::read16(u32 offset)
ValueWithShadow<u16> MmapRegion::read16(u32 offset)
{
if (!is_readable()) {
warn() << "16-bit from unreadable MmapRegion @ " << (const void*)(base() + offset);
@ -99,10 +101,10 @@ u16 MmapRegion::read16(u32 offset)
}
ASSERT(offset + 1 < size());
return *reinterpret_cast<const u16*>(m_data + offset);
return { *reinterpret_cast<const u16*>(m_data + offset), *reinterpret_cast<const u16*>(m_shadow_data + offset) };
}
u32 MmapRegion::read32(u32 offset)
ValueWithShadow<u32> MmapRegion::read32(u32 offset)
{
if (!is_readable()) {
warn() << "32-bit read from unreadable MmapRegion @ " << (const void*)(base() + offset);
@ -116,10 +118,10 @@ u32 MmapRegion::read32(u32 offset)
}
ASSERT(offset + 3 < size());
return *reinterpret_cast<const u32*>(m_data + offset);
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
void MmapRegion::write8(u32 offset, u8 value)
void MmapRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
if (!is_writable()) {
warn() << "8-bit write to unreadable MmapRegion @ " << (const void*)(base() + offset);
@ -133,10 +135,11 @@ void MmapRegion::write8(u32 offset, u8 value)
}
ASSERT(offset < size());
*reinterpret_cast<u8*>(m_data + offset) = value;
*reinterpret_cast<u8*>(m_data + offset) = value.value();
*reinterpret_cast<u8*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::write16(u32 offset, u16 value)
void MmapRegion::write16(u32 offset, ValueWithShadow<u16> value)
{
if (!is_writable()) {
warn() << "16-bit write to unreadable MmapRegion @ " << (const void*)(base() + offset);
@ -150,10 +153,11 @@ void MmapRegion::write16(u32 offset, u16 value)
}
ASSERT(offset + 1 < size());
*reinterpret_cast<u16*>(m_data + offset) = value;
*reinterpret_cast<u16*>(m_data + offset) = value.value();
*reinterpret_cast<u16*>(m_shadow_data + offset) = value.shadow();
}
void MmapRegion::write32(u32 offset, u32 value)
void MmapRegion::write32(u32 offset, ValueWithShadow<u32> value)
{
if (!is_writable()) {
warn() << "32-bit write to unreadable MmapRegion @ " << (const void*)(base() + offset);
@ -167,7 +171,9 @@ void MmapRegion::write32(u32 offset, u32 value)
}
ASSERT(offset + 3 < size());
*reinterpret_cast<u32*>(m_data + offset) = value;
ASSERT(m_data != m_shadow_data);
*reinterpret_cast<u32*>(m_data + offset) = value.value();
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
}

View file

@ -37,15 +37,16 @@ public:
static NonnullOwnPtr<MmapRegion> create_file_backed(u32 base, u32 size, u32 prot, int flags, int fd, off_t offset);
virtual ~MmapRegion() override;
virtual u8 read8(u32 offset) override;
virtual u16 read16(u32 offset) override;
virtual u32 read32(u32 offset) override;
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual void write8(u32 offset, u8 value) override;
virtual void write16(u32 offset, u16 value) override;
virtual void write32(u32 offset, u32 value) 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;
u8* data() { return m_data; }
u8* shadow_data() { return m_shadow_data; }
bool is_readable() const { return m_prot & PROT_READ; }
bool is_writable() const { return m_prot & PROT_WRITE; }
@ -58,6 +59,7 @@ private:
virtual bool is_mmap() const override { return true; }
u8* m_data { nullptr };
u8* m_shadow_data { nullptr };
int m_prot { 0 };
bool m_file_backed { false };
};

View file

@ -42,46 +42,51 @@ SharedBufferRegion::SharedBufferRegion(u32 base, u32 size, int shbuf_id, u8* hos
, m_data(host_data)
, m_shbuf_id(shbuf_id)
{
m_shadow_data = (u8*)calloc(1, size);
}
SharedBufferRegion::~SharedBufferRegion()
{
free(m_shadow_data);
}
u8 SharedBufferRegion::read8(FlatPtr offset)
ValueWithShadow<u8> SharedBufferRegion::read8(FlatPtr offset)
{
ASSERT(offset < size());
return *reinterpret_cast<const u8*>(m_data + offset);
return { *reinterpret_cast<const u8*>(m_data + offset), *reinterpret_cast<const u8*>(m_shadow_data + offset) };
}
u16 SharedBufferRegion::read16(u32 offset)
ValueWithShadow<u16> SharedBufferRegion::read16(u32 offset)
{
ASSERT(offset + 1 < size());
return *reinterpret_cast<const u16*>(m_data + offset);
return { *reinterpret_cast<const u16*>(m_data + offset), *reinterpret_cast<const u16*>(m_shadow_data + offset) };
}
u32 SharedBufferRegion::read32(u32 offset)
ValueWithShadow<u32> SharedBufferRegion::read32(u32 offset)
{
ASSERT(offset + 3 < size());
return *reinterpret_cast<const u32*>(m_data + offset);
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
void SharedBufferRegion::write8(u32 offset, u8 value)
void SharedBufferRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
ASSERT(offset < size());
*reinterpret_cast<u8*>(m_data + offset) = value;
*reinterpret_cast<u8*>(m_data + offset) = value.value();
*reinterpret_cast<u8*>(m_shadow_data + offset) = value.shadow();
}
void SharedBufferRegion::write16(u32 offset, u16 value)
void SharedBufferRegion::write16(u32 offset, ValueWithShadow<u16> value)
{
ASSERT(offset + 1 < size());
*reinterpret_cast<u16*>(m_data + offset) = value;
*reinterpret_cast<u16*>(m_data + offset) = value.value();
*reinterpret_cast<u16*>(m_shadow_data + offset) = value.shadow();
}
void SharedBufferRegion::write32(u32 offset, u32 value)
void SharedBufferRegion::write32(u32 offset, ValueWithShadow<u32> value)
{
ASSERT(offset + 3 < size());
*reinterpret_cast<u32*>(m_data + offset) = value;
*reinterpret_cast<u32*>(m_data + offset) = value.value();
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
int SharedBufferRegion::allow_all()

View file

@ -36,13 +36,13 @@ public:
static NonnullOwnPtr<SharedBufferRegion> create_with_shbuf_id(u32 base, u32 size, int shbuf_id, u8* shbuf_data);
virtual ~SharedBufferRegion() override;
virtual u8 read8(u32 offset) override;
virtual u16 read16(u32 offset) override;
virtual u32 read32(u32 offset) override;
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual void write8(u32 offset, u8 value) override;
virtual void write16(u32 offset, u16 value) override;
virtual void write32(u32 offset, u32 value) 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;
u8* data() { return m_data; }
@ -60,6 +60,7 @@ private:
SharedBufferRegion(u32 base, u32 size, int shbuf_id, u8* shbuf_data);
u8* m_data { nullptr };
u8* m_shadow_data { nullptr };
int m_shbuf_id { 0 };
};

View file

@ -32,47 +32,52 @@ SimpleRegion::SimpleRegion(u32 base, u32 size)
: Region(base, size)
{
m_data = (u8*)calloc(1, size);
m_shadow_data = (u8*)calloc(1, size);
}
SimpleRegion::~SimpleRegion()
{
free(m_shadow_data);
free(m_data);
}
u8 SimpleRegion::read8(FlatPtr offset)
ValueWithShadow<u8> SimpleRegion::read8(FlatPtr offset)
{
ASSERT(offset < size());
return *reinterpret_cast<const u8*>(m_data + offset);
return { *reinterpret_cast<const u8*>(m_data + offset), *reinterpret_cast<const u8*>(m_shadow_data + offset) };
}
u16 SimpleRegion::read16(u32 offset)
ValueWithShadow<u16> SimpleRegion::read16(u32 offset)
{
ASSERT(offset + 1 < size());
return *reinterpret_cast<const u16*>(m_data + offset);
return { *reinterpret_cast<const u16*>(m_data + offset), *reinterpret_cast<const u16*>(m_shadow_data + offset) };
}
u32 SimpleRegion::read32(u32 offset)
ValueWithShadow<u32> SimpleRegion::read32(u32 offset)
{
ASSERT(offset + 3 < size());
return *reinterpret_cast<const u32*>(m_data + offset);
return { *reinterpret_cast<const u32*>(m_data + offset), *reinterpret_cast<const u32*>(m_shadow_data + offset) };
}
void SimpleRegion::write8(u32 offset, u8 value)
void SimpleRegion::write8(u32 offset, ValueWithShadow<u8> value)
{
ASSERT(offset < size());
*reinterpret_cast<u8*>(m_data + offset) = value;
*reinterpret_cast<u8*>(m_data + offset) = value.value();
*reinterpret_cast<u8*>(m_shadow_data + offset) = value.shadow();
}
void SimpleRegion::write16(u32 offset, u16 value)
void SimpleRegion::write16(u32 offset, ValueWithShadow<u16> value)
{
ASSERT(offset + 1 < size());
*reinterpret_cast<u16*>(m_data + offset) = value;
*reinterpret_cast<u16*>(m_data + offset) = value.value();
*reinterpret_cast<u16*>(m_shadow_data + offset) = value.shadow();
}
void SimpleRegion::write32(u32 offset, u32 value)
void SimpleRegion::write32(u32 offset, ValueWithShadow<u32> value)
{
ASSERT(offset + 3 < size());
*reinterpret_cast<u32*>(m_data + offset) = value;
*reinterpret_cast<u32*>(m_data + offset) = value.value();
*reinterpret_cast<u32*>(m_shadow_data + offset) = value.shadow();
}
u8* SimpleRegion::cacheable_ptr(u32 offset)

View file

@ -35,20 +35,22 @@ public:
SimpleRegion(u32 base, u32 size);
virtual ~SimpleRegion() override;
virtual u8 read8(u32 offset) override;
virtual u16 read16(u32 offset) override;
virtual u32 read32(u32 offset) override;
virtual ValueWithShadow<u8> read8(u32 offset) override;
virtual ValueWithShadow<u16> read16(u32 offset) override;
virtual ValueWithShadow<u32> read32(u32 offset) override;
virtual void write8(u32 offset, u8 value) override;
virtual void write16(u32 offset, u16 value) override;
virtual void write32(u32 offset, u32 value) 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;
u8* data() { return m_data; }
u8* shadow_data() { return m_shadow_data; }
virtual u8* cacheable_ptr(u32 offset) override;
private:
u8* m_data { nullptr };
u8* m_shadow_data { nullptr };
};
}

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,7 @@
#pragma once
#include "ValueWithShadow.h"
#include <LibX86/Instruction.h>
#include <LibX86/Interpreter.h>
@ -77,71 +78,96 @@ public:
};
};
void push32(u32);
u32 pop32();
void push32(ValueWithShadow<u32>);
ValueWithShadow<u32> pop32();
void push16(u16);
u16 pop16();
void push16(ValueWithShadow<u16>);
ValueWithShadow<u16> pop16();
void push_string(const StringView&);
u16 segment(X86::SegmentRegister seg) const { return m_segment[(int)seg]; }
u16& segment(X86::SegmentRegister seg) { return m_segment[(int)seg]; }
u8& gpr8(X86::RegisterIndex8 reg)
ValueAndShadowReference<u8> gpr8(X86::RegisterIndex8 reg)
{
switch (reg) {
case X86::RegisterAL:
return m_gpr[X86::RegisterEAX].low_u8;
return { m_gpr[X86::RegisterEAX].low_u8, m_gpr_shadow[X86::RegisterEAX].low_u8 };
case X86::RegisterAH:
return m_gpr[X86::RegisterEAX].high_u8;
return { m_gpr[X86::RegisterEAX].high_u8, m_gpr_shadow[X86::RegisterEAX].high_u8 };
case X86::RegisterBL:
return m_gpr[X86::RegisterEBX].low_u8;
return { m_gpr[X86::RegisterEBX].low_u8, m_gpr_shadow[X86::RegisterEBX].low_u8 };
case X86::RegisterBH:
return m_gpr[X86::RegisterEBX].high_u8;
return { m_gpr[X86::RegisterEBX].high_u8, m_gpr_shadow[X86::RegisterEBX].high_u8 };
case X86::RegisterCL:
return m_gpr[X86::RegisterECX].low_u8;
return { m_gpr[X86::RegisterECX].low_u8, m_gpr_shadow[X86::RegisterECX].low_u8 };
case X86::RegisterCH:
return m_gpr[X86::RegisterECX].high_u8;
return { m_gpr[X86::RegisterECX].high_u8, m_gpr_shadow[X86::RegisterECX].high_u8 };
case X86::RegisterDL:
return m_gpr[X86::RegisterEDX].low_u8;
return { m_gpr[X86::RegisterEDX].low_u8, m_gpr_shadow[X86::RegisterEDX].low_u8 };
case X86::RegisterDH:
return m_gpr[X86::RegisterEDX].high_u8;
return { m_gpr[X86::RegisterEDX].high_u8, m_gpr_shadow[X86::RegisterEDX].high_u8 };
}
ASSERT_NOT_REACHED();
}
u8 gpr8(X86::RegisterIndex8 reg) const
ValueWithShadow<u8> const_gpr8(X86::RegisterIndex8 reg) const
{
switch (reg) {
case X86::RegisterAL:
return m_gpr[X86::RegisterEAX].low_u8;
return { m_gpr[X86::RegisterEAX].low_u8, m_gpr_shadow[X86::RegisterEAX].low_u8 };
case X86::RegisterAH:
return m_gpr[X86::RegisterEAX].high_u8;
return { m_gpr[X86::RegisterEAX].high_u8, m_gpr_shadow[X86::RegisterEAX].high_u8 };
case X86::RegisterBL:
return m_gpr[X86::RegisterEBX].low_u8;
return { m_gpr[X86::RegisterEBX].low_u8, m_gpr_shadow[X86::RegisterEBX].low_u8 };
case X86::RegisterBH:
return m_gpr[X86::RegisterEBX].high_u8;
return { m_gpr[X86::RegisterEBX].high_u8, m_gpr_shadow[X86::RegisterEBX].high_u8 };
case X86::RegisterCL:
return m_gpr[X86::RegisterECX].low_u8;
return { m_gpr[X86::RegisterECX].low_u8, m_gpr_shadow[X86::RegisterECX].low_u8 };
case X86::RegisterCH:
return m_gpr[X86::RegisterECX].high_u8;
return { m_gpr[X86::RegisterECX].high_u8, m_gpr_shadow[X86::RegisterECX].high_u8 };
case X86::RegisterDL:
return m_gpr[X86::RegisterEDX].low_u8;
return { m_gpr[X86::RegisterEDX].low_u8, m_gpr_shadow[X86::RegisterEDX].low_u8 };
case X86::RegisterDH:
return m_gpr[X86::RegisterEDX].high_u8;
return { m_gpr[X86::RegisterEDX].high_u8, m_gpr_shadow[X86::RegisterEDX].high_u8 };
}
ASSERT_NOT_REACHED();
}
u16 gpr16(X86::RegisterIndex16 reg) const { return m_gpr[reg].low_u16; }
u16& gpr16(X86::RegisterIndex16 reg) { return m_gpr[reg].low_u16; }
ValueWithShadow<u16> const_gpr16(X86::RegisterIndex16 reg) const
{
return { m_gpr[reg].low_u16, m_gpr_shadow[reg].low_u16 };
}
u32 gpr32(X86::RegisterIndex32 reg) const { return m_gpr[reg].full_u32; }
u32& gpr32(X86::RegisterIndex32 reg) { return m_gpr[reg].full_u32; }
ValueAndShadowReference<u16> gpr16(X86::RegisterIndex16 reg)
{
return { m_gpr[reg].low_u16, m_gpr_shadow[reg].low_u16 };
}
ValueWithShadow<u32> const_gpr32(X86::RegisterIndex32 reg) const
{
return { m_gpr[reg].full_u32, m_gpr_shadow[reg].full_u32 };
}
ValueAndShadowReference<u32> gpr32(X86::RegisterIndex32 reg)
{
return { m_gpr[reg].full_u32, m_gpr_shadow[reg].full_u32 };
}
template<typename T>
T gpr(unsigned register_index) const
ValueWithShadow<T> const_gpr(unsigned register_index) const
{
if constexpr (sizeof(T) == 1)
return const_gpr8((X86::RegisterIndex8)register_index);
if constexpr (sizeof(T) == 2)
return const_gpr16((X86::RegisterIndex16)register_index);
if constexpr (sizeof(T) == 4)
return const_gpr32((X86::RegisterIndex32)register_index);
}
template<typename T>
ValueAndShadowReference<T> gpr(unsigned register_index)
{
if constexpr (sizeof(T) == 1)
return gpr8((X86::RegisterIndex8)register_index);
@ -151,60 +177,49 @@ public:
return gpr32((X86::RegisterIndex32)register_index);
}
template<typename T>
T& gpr(unsigned register_index)
{
if constexpr (sizeof(T) == 1)
return gpr8((X86::RegisterIndex8)register_index);
if constexpr (sizeof(T) == 2)
return gpr16((X86::RegisterIndex16)register_index);
if constexpr (sizeof(T) == 4)
return gpr32((X86::RegisterIndex32)register_index);
}
u32 source_index(bool a32) const
ValueWithShadow<u32> source_index(bool a32) const
{
if (a32)
return esi();
return si();
return { si().value(), (u32)si().shadow() & 0xffff };
}
u32 destination_index(bool a32) const
ValueWithShadow<u32> destination_index(bool a32) const
{
if (a32)
return edi();
return di();
return { di().value(), (u32)di().shadow() & 0xffff };
}
u32 loop_index(bool a32) const
ValueWithShadow<u32> loop_index(bool a32) const
{
if (a32)
return ecx();
return cx();
return { cx().value(), (u32)cx().shadow() & 0xffff };
}
bool decrement_loop_index(bool a32)
{
if (a32) {
set_ecx(ecx() - 1);
return ecx() == 0;
set_ecx({ ecx().value() - 1, ecx().shadow() });
return ecx().value() == 0;
}
set_cx(cx() - 1);
return cx() == 0;
set_cx(ValueWithShadow<u16>(cx().value() - 1, cx().shadow()));
return cx().value() == 0;
}
ALWAYS_INLINE void step_source_index(bool a32, u32 step)
{
if (a32) {
if (df())
set_esi(esi() - step);
set_esi({ esi().value() - step, esi().shadow() });
else
set_esi(esi() + step);
set_esi({ esi().value() + step, esi().shadow() });
} else {
if (df())
set_si(si() - step);
set_si(ValueWithShadow<u16>(si().value() - step, si().shadow()));
else
set_si(si() + step);
set_si(ValueWithShadow<u16>(si().value() + step, si().shadow()));
}
}
@ -212,71 +227,70 @@ public:
{
if (a32) {
if (df())
set_edi(edi() - step);
set_edi({ edi().value() - step, edi().shadow() });
else
set_edi(edi() + step);
set_edi({ edi().value() + step, edi().shadow() });
} else {
if (df())
set_di(di() - step);
set_di(ValueWithShadow<u16>(di().value() - step, di().shadow()));
else
set_di(di() + step);
set_di(ValueWithShadow<u16>(di().value() + step, di().shadow()));
}
}
ValueWithShadow<u32> eax() const { return const_gpr32(X86::RegisterEAX); }
ValueWithShadow<u32> ebx() const { return const_gpr32(X86::RegisterEBX); }
ValueWithShadow<u32> ecx() const { return const_gpr32(X86::RegisterECX); }
ValueWithShadow<u32> edx() const { return const_gpr32(X86::RegisterEDX); }
ValueWithShadow<u32> esp() const { return const_gpr32(X86::RegisterESP); }
ValueWithShadow<u32> ebp() const { return const_gpr32(X86::RegisterEBP); }
ValueWithShadow<u32> esi() const { return const_gpr32(X86::RegisterESI); }
ValueWithShadow<u32> edi() const { return const_gpr32(X86::RegisterEDI); }
u32 eax() const { return gpr32(X86::RegisterEAX); }
u32 ebx() const { return gpr32(X86::RegisterEBX); }
u32 ecx() const { return gpr32(X86::RegisterECX); }
u32 edx() const { return gpr32(X86::RegisterEDX); }
u32 esp() const { return gpr32(X86::RegisterESP); }
u32 ebp() const { return gpr32(X86::RegisterEBP); }
u32 esi() const { return gpr32(X86::RegisterESI); }
u32 edi() const { return gpr32(X86::RegisterEDI); }
ValueWithShadow<u16> ax() const { return const_gpr16(X86::RegisterAX); }
ValueWithShadow<u16> bx() const { return const_gpr16(X86::RegisterBX); }
ValueWithShadow<u16> cx() const { return const_gpr16(X86::RegisterCX); }
ValueWithShadow<u16> dx() const { return const_gpr16(X86::RegisterDX); }
ValueWithShadow<u16> sp() const { return const_gpr16(X86::RegisterSP); }
ValueWithShadow<u16> bp() const { return const_gpr16(X86::RegisterBP); }
ValueWithShadow<u16> si() const { return const_gpr16(X86::RegisterSI); }
ValueWithShadow<u16> di() const { return const_gpr16(X86::RegisterDI); }
u16 ax() const { return gpr16(X86::RegisterAX); }
u16 bx() const { return gpr16(X86::RegisterBX); }
u16 cx() const { return gpr16(X86::RegisterCX); }
u16 dx() const { return gpr16(X86::RegisterDX); }
u16 sp() const { return gpr16(X86::RegisterSP); }
u16 bp() const { return gpr16(X86::RegisterBP); }
u16 si() const { return gpr16(X86::RegisterSI); }
u16 di() const { return gpr16(X86::RegisterDI); }
ValueWithShadow<u8> al() const { return const_gpr8(X86::RegisterAL); }
ValueWithShadow<u8> ah() const { return const_gpr8(X86::RegisterAH); }
ValueWithShadow<u8> bl() const { return const_gpr8(X86::RegisterBL); }
ValueWithShadow<u8> bh() const { return const_gpr8(X86::RegisterBH); }
ValueWithShadow<u8> cl() const { return const_gpr8(X86::RegisterCL); }
ValueWithShadow<u8> ch() const { return const_gpr8(X86::RegisterCH); }
ValueWithShadow<u8> dl() const { return const_gpr8(X86::RegisterDL); }
ValueWithShadow<u8> dh() const { return const_gpr8(X86::RegisterDH); }
u8 al() const { return gpr8(X86::RegisterAL); }
u8 ah() const { return gpr8(X86::RegisterAH); }
u8 bl() const { return gpr8(X86::RegisterBL); }
u8 bh() const { return gpr8(X86::RegisterBH); }
u8 cl() const { return gpr8(X86::RegisterCL); }
u8 ch() const { return gpr8(X86::RegisterCH); }
u8 dl() const { return gpr8(X86::RegisterDL); }
u8 dh() const { return gpr8(X86::RegisterDH); }
void set_eax(ValueWithShadow<u32> value) { gpr32(X86::RegisterEAX) = value; }
void set_ebx(ValueWithShadow<u32> value) { gpr32(X86::RegisterEBX) = value; }
void set_ecx(ValueWithShadow<u32> value) { gpr32(X86::RegisterECX) = value; }
void set_edx(ValueWithShadow<u32> value) { gpr32(X86::RegisterEDX) = value; }
void set_esp(ValueWithShadow<u32> value) { gpr32(X86::RegisterESP) = value; }
void set_ebp(ValueWithShadow<u32> value) { gpr32(X86::RegisterEBP) = value; }
void set_esi(ValueWithShadow<u32> value) { gpr32(X86::RegisterESI) = value; }
void set_edi(ValueWithShadow<u32> value) { gpr32(X86::RegisterEDI) = value; }
void set_eax(u32 value) { gpr32(X86::RegisterEAX) = value; }
void set_ebx(u32 value) { gpr32(X86::RegisterEBX) = value; }
void set_ecx(u32 value) { gpr32(X86::RegisterECX) = value; }
void set_edx(u32 value) { gpr32(X86::RegisterEDX) = value; }
void set_esp(u32 value) { gpr32(X86::RegisterESP) = value; }
void set_ebp(u32 value) { gpr32(X86::RegisterEBP) = value; }
void set_esi(u32 value) { gpr32(X86::RegisterESI) = value; }
void set_edi(u32 value) { gpr32(X86::RegisterEDI) = value; }
void set_ax(ValueWithShadow<u16> value) { gpr16(X86::RegisterAX) = value; }
void set_bx(ValueWithShadow<u16> value) { gpr16(X86::RegisterBX) = value; }
void set_cx(ValueWithShadow<u16> value) { gpr16(X86::RegisterCX) = value; }
void set_dx(ValueWithShadow<u16> value) { gpr16(X86::RegisterDX) = value; }
void set_sp(ValueWithShadow<u16> value) { gpr16(X86::RegisterSP) = value; }
void set_bp(ValueWithShadow<u16> value) { gpr16(X86::RegisterBP) = value; }
void set_si(ValueWithShadow<u16> value) { gpr16(X86::RegisterSI) = value; }
void set_di(ValueWithShadow<u16> value) { gpr16(X86::RegisterDI) = value; }
void set_ax(u16 value) { gpr16(X86::RegisterAX) = value; }
void set_bx(u16 value) { gpr16(X86::RegisterBX) = value; }
void set_cx(u16 value) { gpr16(X86::RegisterCX) = value; }
void set_dx(u16 value) { gpr16(X86::RegisterDX) = value; }
void set_sp(u16 value) { gpr16(X86::RegisterSP) = value; }
void set_bp(u16 value) { gpr16(X86::RegisterBP) = value; }
void set_si(u16 value) { gpr16(X86::RegisterSI) = value; }
void set_di(u16 value) { gpr16(X86::RegisterDI) = value; }
void set_al(u8 value) { gpr8(X86::RegisterAL) = value; }
void set_ah(u8 value) { gpr8(X86::RegisterAH) = value; }
void set_bl(u8 value) { gpr8(X86::RegisterBL) = value; }
void set_bh(u8 value) { gpr8(X86::RegisterBH) = value; }
void set_cl(u8 value) { gpr8(X86::RegisterCL) = value; }
void set_ch(u8 value) { gpr8(X86::RegisterCH) = value; }
void set_dl(u8 value) { gpr8(X86::RegisterDL) = value; }
void set_dh(u8 value) { gpr8(X86::RegisterDH) = value; }
void set_al(ValueWithShadow<u8> value) { gpr8(X86::RegisterAL) = value; }
void set_ah(ValueWithShadow<u8> value) { gpr8(X86::RegisterAH) = value; }
void set_bl(ValueWithShadow<u8> value) { gpr8(X86::RegisterBL) = value; }
void set_bh(ValueWithShadow<u8> value) { gpr8(X86::RegisterBH) = value; }
void set_cl(ValueWithShadow<u8> value) { gpr8(X86::RegisterCL) = value; }
void set_ch(ValueWithShadow<u8> value) { gpr8(X86::RegisterCH) = value; }
void set_dl(ValueWithShadow<u8> value) { gpr8(X86::RegisterDL) = value; }
void set_dh(ValueWithShadow<u8> value) { gpr8(X86::RegisterDH) = value; }
bool of() const { return m_eflags & Flags::OF; }
bool sf() const { return m_eflags & Flags::SF; }
@ -333,12 +347,12 @@ public:
u16 es() const { return m_segment[(int)X86::SegmentRegister::ES]; }
u16 ss() const { return m_segment[(int)X86::SegmentRegister::SS]; }
u8 read_memory8(X86::LogicalAddress);
u16 read_memory16(X86::LogicalAddress);
u32 read_memory32(X86::LogicalAddress);
ValueWithShadow<u8> read_memory8(X86::LogicalAddress);
ValueWithShadow<u16> read_memory16(X86::LogicalAddress);
ValueWithShadow<u32> read_memory32(X86::LogicalAddress);
template<typename T>
T read_memory(X86::LogicalAddress address)
ValueWithShadow<T> read_memory(X86::LogicalAddress address)
{
if constexpr (sizeof(T) == 1)
return read_memory8(address);
@ -348,12 +362,12 @@ public:
return read_memory32(address);
}
void write_memory8(X86::LogicalAddress, u8);
void write_memory16(X86::LogicalAddress, u16);
void write_memory32(X86::LogicalAddress, u32);
void write_memory8(X86::LogicalAddress, ValueWithShadow<u8>);
void write_memory16(X86::LogicalAddress, ValueWithShadow<u16>);
void write_memory32(X86::LogicalAddress, ValueWithShadow<u32>);
template<typename T>
void write_memory(X86::LogicalAddress address, T data)
void write_memory(X86::LogicalAddress address, ValueWithShadow<T> data)
{
if constexpr (sizeof(T) == 1)
return write_memory8(address, data);
@ -896,12 +910,16 @@ private:
template<bool update_dest, typename Op>
void generic_RM16_imm8(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM16_unsigned_imm8(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM16_reg16(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM32_imm32(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM32_imm8(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM32_unsigned_imm8(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM32_reg32(Op, const X86::Instruction&);
template<bool update_dest, typename Op>
void generic_RM8_imm8(Op, const X86::Instruction&);
@ -935,6 +953,8 @@ private:
Emulator& m_emulator;
PartAddressableRegister m_gpr[8];
PartAddressableRegister m_gpr_shadow[8];
u16 m_segment[8] { 0 };
u32 m_eflags { 0 };

View file

@ -64,7 +64,7 @@ void SoftMMU::set_tls_region(NonnullOwnPtr<Region> region)
m_tls_region = move(region);
}
u8 SoftMMU::read8(X86::LogicalAddress address)
ValueWithShadow<u8> SoftMMU::read8(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
@ -75,7 +75,7 @@ u8 SoftMMU::read8(X86::LogicalAddress address)
return region->read8(address.offset() - region->base());
}
u16 SoftMMU::read16(X86::LogicalAddress address)
ValueWithShadow<u16> SoftMMU::read16(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
@ -86,7 +86,7 @@ u16 SoftMMU::read16(X86::LogicalAddress address)
return region->read16(address.offset() - region->base());
}
u32 SoftMMU::read32(X86::LogicalAddress address)
ValueWithShadow<u32> SoftMMU::read32(X86::LogicalAddress address)
{
auto* region = find_region(address);
if (!region) {
@ -97,7 +97,7 @@ u32 SoftMMU::read32(X86::LogicalAddress address)
return region->read32(address.offset() - region->base());
}
void SoftMMU::write8(X86::LogicalAddress address, u8 value)
void SoftMMU::write8(X86::LogicalAddress address, ValueWithShadow<u8> value)
{
auto* region = find_region(address);
if (!region) {
@ -108,7 +108,7 @@ void SoftMMU::write8(X86::LogicalAddress address, u8 value)
region->write8(address.offset() - region->base(), value);
}
void SoftMMU::write16(X86::LogicalAddress address, u16 value)
void SoftMMU::write16(X86::LogicalAddress address, ValueWithShadow<u16> value)
{
auto* region = find_region(address);
if (!region) {
@ -119,7 +119,7 @@ void SoftMMU::write16(X86::LogicalAddress address, u16 value)
region->write16(address.offset() - region->base(), value);
}
void SoftMMU::write32(X86::LogicalAddress address, u32 value)
void SoftMMU::write32(X86::LogicalAddress address, ValueWithShadow<u32> value)
{
auto* region = find_region(address);
if (!region) {
@ -132,14 +132,16 @@ void SoftMMU::write32(X86::LogicalAddress address, u32 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.
for (size_t i = 0; i < size; ++i)
write8({ 0x20, destination + i }, ((const u8*)source)[i]);
write8({ 0x20, destination + i }, shadow_wrap_as_initialized(((const u8*)source)[i]));
}
void SoftMMU::copy_from_vm(void* destination, const FlatPtr source, size_t size)
{
// FIXME: We should have a way to preserve the shadow data here as well.
for (size_t i = 0; i < size; ++i)
((u8*)destination)[i] = read8({ 0x20, source + i });
((u8*)destination)[i] = read8({ 0x20, source + i }).value();
}
ByteBuffer SoftMMU::copy_buffer_from_vm(const FlatPtr source, size_t size)

View file

@ -26,6 +26,7 @@
#pragma once
#include "ValueWithShadow.h"
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtrVector.h>
#include <AK/OwnPtr.h>
@ -48,13 +49,13 @@ public:
bool contains(u32 address) const { return address >= base() && address < end(); }
virtual void write8(u32 offset, u8 value) = 0;
virtual void write16(u32 offset, u16 value) = 0;
virtual void write32(u32 offset, u32 value) = 0;
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 u8 read8(u32 offset) = 0;
virtual u16 read16(u32 offset) = 0;
virtual u32 read32(u32 offset) = 0;
virtual ValueWithShadow<u8> read8(u32 offset) = 0;
virtual ValueWithShadow<u16> read16(u32 offset) = 0;
virtual ValueWithShadow<u32> read32(u32 offset) = 0;
virtual u8* cacheable_ptr([[maybe_unused]] u32 offset) { return nullptr; }
virtual bool is_shared_buffer() const { return false; }
@ -81,13 +82,13 @@ public:
bool m_text { false };
};
u8 read8(X86::LogicalAddress);
u16 read16(X86::LogicalAddress);
u32 read32(X86::LogicalAddress);
ValueWithShadow<u8> read8(X86::LogicalAddress);
ValueWithShadow<u16> read16(X86::LogicalAddress);
ValueWithShadow<u32> read32(X86::LogicalAddress);
void write8(X86::LogicalAddress, u8);
void write16(X86::LogicalAddress, u16);
void write32(X86::LogicalAddress, u32);
void write8(X86::LogicalAddress, ValueWithShadow<u8>);
void write16(X86::LogicalAddress, ValueWithShadow<u16>);
void write32(X86::LogicalAddress, ValueWithShadow<u32>);
Region* find_region(X86::LogicalAddress);

View file

@ -0,0 +1,150 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Platform.h>
#pragma once
namespace UserspaceEmulator {
template<typename T>
class ValueAndShadowReference;
template<typename T>
class ValueWithShadow {
public:
using ValueType = T;
ValueWithShadow(T value, T shadow)
: m_value(value)
, m_shadow(shadow)
{
}
ValueWithShadow(const ValueAndShadowReference<T>&);
T value() const { return m_value; }
T shadow() const { return m_shadow; }
bool is_uninitialized() const
{
if constexpr (sizeof(T) == 4)
return (m_shadow & 0x01010101) != 0x01010101;
if constexpr (sizeof(T) == 2)
return (m_shadow & 0x0101) != 0x0101;
if constexpr (sizeof(T) == 1)
return (m_shadow & 0x01) != 0x01;
}
private:
T m_value;
T m_shadow;
};
template<typename T>
class ValueAndShadowReference {
public:
using ValueType = T;
ValueAndShadowReference(T& value, T& shadow)
: m_value(value)
, m_shadow(shadow)
{
}
bool is_uninitialized() const
{
if constexpr (sizeof(T) == 4)
return (m_shadow & 0x01010101) != 0x01010101;
if constexpr (sizeof(T) == 2)
return (m_shadow & 0x0101) != 0x0101;
if constexpr (sizeof(T) == 1)
return (m_shadow & 0x01) != 0x01;
}
void operator=(const ValueWithShadow<T>&);
T& value() { return m_value; }
T& shadow() { return m_shadow; }
const T& value() const { return m_value; }
const T& shadow() const { return m_shadow; }
private:
T& m_value;
T& m_shadow;
};
template<typename T>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_as_initialized(T value)
{
if constexpr (sizeof(T) == 4)
return { value, 0x01010101 };
if constexpr (sizeof(T) == 2)
return { value, 0x0101 };
if constexpr (sizeof(T) == 1)
return { value, 0x01 };
}
template<typename T, typename U>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_with_taint_from(T value, const U& taint_a)
{
if (taint_a.is_uninitialized())
return { value, 0 };
return shadow_wrap_as_initialized(value);
}
template<typename T, typename U, typename V>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_with_taint_from(T value, const U& taint_a, const V& taint_b)
{
if (taint_a.is_uninitialized() || taint_b.is_uninitialized())
return { value, 0 };
return shadow_wrap_as_initialized(value);
}
template<typename T, typename U, typename V, typename X>
ALWAYS_INLINE ValueWithShadow<T> shadow_wrap_with_taint_from(T value, const U& taint_a, const V& taint_b, const X& taint_c)
{
if (taint_a.is_uninitialized() || taint_b.is_uninitialized() || taint_c.is_uninitialized())
return { value, 0 };
return shadow_wrap_as_initialized(value);
}
template<typename T>
inline ValueWithShadow<T>::ValueWithShadow(const ValueAndShadowReference<T>& other)
: m_value(other.value())
, m_shadow(other.shadow())
{
}
template<typename T>
inline void ValueAndShadowReference<T>::operator=(const ValueWithShadow<T>& other)
{
m_value = other.value();
m_shadow = other.shadow();
}
}

View file

@ -348,19 +348,19 @@ public:
RegisterIndex16 reg16() const { return static_cast<RegisterIndex16>(register_index()); }
RegisterIndex8 reg8() const { return static_cast<RegisterIndex8>(register_index()); }
template<typename CPU>
void write8(CPU&, const Instruction&, u8);
template<typename CPU>
void write16(CPU&, const Instruction&, u16);
template<typename CPU>
void write32(CPU&, const Instruction&, u32);
template<typename CPU, typename T>
void write8(CPU&, const Instruction&, T);
template<typename CPU, typename T>
void write16(CPU&, const Instruction&, T);
template<typename CPU, typename T>
void write32(CPU&, const Instruction&, T);
template<typename CPU>
u8 read8(CPU&, const Instruction&);
template<typename CPU>
u16 read16(CPU&, const Instruction&);
template<typename CPU>
u32 read32(CPU&, const Instruction&);
template<typename T, typename CPU>
T read8(CPU&, const Instruction&);
template<typename T, typename CPU>
T read16(CPU&, const Instruction&);
template<typename T, typename CPU>
T read32(CPU&, const Instruction&);
template<typename CPU>
LogicalAddress resolve(const CPU&, const Instruction&);
@ -519,35 +519,35 @@ ALWAYS_INLINE LogicalAddress MemoryOrRegisterReference::resolve16(const CPU& cpu
switch (m_rm & 7) {
case 0:
offset = cpu.bx() + cpu.si() + m_displacement16;
offset = cpu.bx().value() + cpu.si().value() + m_displacement16;
break;
case 1:
offset = cpu.bx() + cpu.di() + m_displacement16;
offset = cpu.bx().value() + cpu.di().value() + m_displacement16;
break;
case 2:
default_segment = SegmentRegister::SS;
offset = cpu.bp() + cpu.si() + m_displacement16;
offset = cpu.bp().value() + cpu.si().value() + m_displacement16;
break;
case 3:
default_segment = SegmentRegister::SS;
offset = cpu.bp() + cpu.di() + m_displacement16;
offset = cpu.bp().value() + cpu.di().value() + m_displacement16;
break;
case 4:
offset = cpu.si() + m_displacement16;
offset = cpu.si().value() + m_displacement16;
break;
case 5:
offset = cpu.di() + m_displacement16;
offset = cpu.di().value() + m_displacement16;
break;
case 6:
if ((m_rm & 0xc0) == 0)
offset = m_displacement16;
else {
default_segment = SegmentRegister::SS;
offset = cpu.bp() + m_displacement16;
offset = cpu.bp().value() + m_displacement16;
}
break;
default:
offset = cpu.bx() + m_displacement16;
offset = cpu.bx().value() + m_displacement16;
break;
}
@ -563,25 +563,25 @@ ALWAYS_INLINE LogicalAddress MemoryOrRegisterReference::resolve32(const CPU& cpu
switch (m_rm & 0x07) {
case 0:
offset = cpu.eax() + m_displacement32;
offset = cpu.eax().value() + m_displacement32;
break;
case 1:
offset = cpu.ecx() + m_displacement32;
offset = cpu.ecx().value() + m_displacement32;
break;
case 2:
offset = cpu.edx() + m_displacement32;
offset = cpu.edx().value() + m_displacement32;
break;
case 3:
offset = cpu.ebx() + m_displacement32;
offset = cpu.ebx().value() + m_displacement32;
break;
case 4:
offset = evaluate_sib(cpu, default_segment);
break;
case 6:
offset = cpu.esi() + m_displacement32;
offset = cpu.esi().value() + m_displacement32;
break;
case 7:
offset = cpu.edi() + m_displacement32;
offset = cpu.edi().value() + m_displacement32;
break;
default: // 5
if ((m_rm & 0xc0) == 0x00) {
@ -589,7 +589,7 @@ ALWAYS_INLINE LogicalAddress MemoryOrRegisterReference::resolve32(const CPU& cpu
break;
} else {
default_segment = SegmentRegister::SS;
offset = cpu.ebp() + m_displacement32;
offset = cpu.ebp().value() + m_displacement32;
break;
}
break;
@ -619,54 +619,54 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, Segmen
u32 index = 0;
switch ((m_sib >> 3) & 0x07) {
case 0:
index = cpu.eax();
index = cpu.eax().value();
break;
case 1:
index = cpu.ecx();
index = cpu.ecx().value();
break;
case 2:
index = cpu.edx();
index = cpu.edx().value();
break;
case 3:
index = cpu.ebx();
index = cpu.ebx().value();
break;
case 4:
index = 0;
break;
case 5:
index = cpu.ebp();
index = cpu.ebp().value();
break;
case 6:
index = cpu.esi();
index = cpu.esi().value();
break;
case 7:
index = cpu.edi();
index = cpu.edi().value();
break;
}
u32 base = m_displacement32;
switch (m_sib & 0x07) {
case 0:
base += cpu.eax();
base += cpu.eax().value();
break;
case 1:
base += cpu.ecx();
base += cpu.ecx().value();
break;
case 2:
base += cpu.edx();
base += cpu.edx().value();
break;
case 3:
base += cpu.ebx();
base += cpu.ebx().value();
break;
case 4:
default_segment = SegmentRegister::SS;
base += cpu.esp();
base += cpu.esp().value();
break;
case 6:
base += cpu.esi();
base += cpu.esi().value();
break;
case 7:
base += cpu.edi();
base += cpu.edi().value();
break;
default: // 5
switch ((m_rm >> 6) & 3) {
@ -675,7 +675,7 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, Segmen
case 1:
case 2:
default_segment = SegmentRegister::SS;
base += cpu.ebp();
base += cpu.ebp().value();
break;
default:
ASSERT_NOT_REACHED();
@ -687,8 +687,8 @@ ALWAYS_INLINE u32 MemoryOrRegisterReference::evaluate_sib(const CPU& cpu, Segmen
return (scale * index) + base;
}
template<typename CPU>
ALWAYS_INLINE void MemoryOrRegisterReference::write8(CPU& cpu, const Instruction& insn, u8 value)
template<typename CPU, typename T>
ALWAYS_INLINE void MemoryOrRegisterReference::write8(CPU& cpu, const Instruction& insn, T value)
{
if (is_register()) {
cpu.gpr8(reg8()) = value;
@ -699,8 +699,8 @@ ALWAYS_INLINE void MemoryOrRegisterReference::write8(CPU& cpu, const Instruction
cpu.write_memory8(address, value);
}
template<typename CPU>
ALWAYS_INLINE void MemoryOrRegisterReference::write16(CPU& cpu, const Instruction& insn, u16 value)
template<typename CPU, typename T>
ALWAYS_INLINE void MemoryOrRegisterReference::write16(CPU& cpu, const Instruction& insn, T value)
{
if (is_register()) {
cpu.gpr16(reg16()) = value;
@ -711,8 +711,8 @@ ALWAYS_INLINE void MemoryOrRegisterReference::write16(CPU& cpu, const Instructio
cpu.write_memory16(address, value);
}
template<typename CPU>
ALWAYS_INLINE void MemoryOrRegisterReference::write32(CPU& cpu, const Instruction& insn, u32 value)
template<typename CPU, typename T>
ALWAYS_INLINE void MemoryOrRegisterReference::write32(CPU& cpu, const Instruction& insn, T value)
{
if (is_register()) {
cpu.gpr32(reg32()) = value;
@ -723,31 +723,31 @@ ALWAYS_INLINE void MemoryOrRegisterReference::write32(CPU& cpu, const Instructio
cpu.write_memory32(address, value);
}
template<typename CPU>
ALWAYS_INLINE u8 MemoryOrRegisterReference::read8(CPU& cpu, const Instruction& insn)
template<typename T, typename CPU>
ALWAYS_INLINE T MemoryOrRegisterReference::read8(CPU& cpu, const Instruction& insn)
{
if (is_register())
return cpu.gpr8(reg8());
return cpu.const_gpr8(reg8());
auto address = resolve(cpu, insn);
return cpu.read_memory8(address);
}
template<typename CPU>
ALWAYS_INLINE u16 MemoryOrRegisterReference::read16(CPU& cpu, const Instruction& insn)
template<typename T, typename CPU>
ALWAYS_INLINE T MemoryOrRegisterReference::read16(CPU& cpu, const Instruction& insn)
{
if (is_register())
return cpu.gpr16(reg16());
return cpu.const_gpr16(reg16());
auto address = resolve(cpu, insn);
return cpu.read_memory16(address);
}
template<typename CPU>
ALWAYS_INLINE u32 MemoryOrRegisterReference::read32(CPU& cpu, const Instruction& insn)
template<typename T, typename CPU>
ALWAYS_INLINE T MemoryOrRegisterReference::read32(CPU& cpu, const Instruction& insn)
{
if (is_register())
return cpu.gpr32(reg32());
return cpu.const_gpr32(reg32());
auto address = resolve(cpu, insn);
return cpu.read_memory32(address);