mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-10 13:00:29 +03:00
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:
parent
1fa5a526e8
commit
f1c0f661f4
Notes:
sideshowbarker
2024-07-19 02:16:09 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/f1c0f661f44 Pull-request: https://github.com/SerenityOS/serenity/pull/3584
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user