mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-19 03:33:56 +00:00
Format all the code
This commit is contained in:
@@ -4,52 +4,52 @@
|
||||
template <typename T>
|
||||
T* offset_pointer(void* data, const size_t offset)
|
||||
{
|
||||
return reinterpret_cast<T*>(static_cast<uint8_t*>(data) + offset);
|
||||
return reinterpret_cast<T*>(static_cast<uint8_t*>(data) + offset);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* offset_pointer(const void* data, const size_t offset)
|
||||
{
|
||||
return reinterpret_cast<const T*>(static_cast<const uint8_t*>(data) + offset);
|
||||
return reinterpret_cast<const T*>(static_cast<const uint8_t*>(data) + offset);
|
||||
}
|
||||
|
||||
constexpr bool is_within_start_and_end(const uint64_t value, const uint64_t start, const uint64_t end)
|
||||
{
|
||||
return value >= start && value < end;
|
||||
return value >= start && value < end;
|
||||
}
|
||||
|
||||
constexpr bool is_within_start_and_length(const uint64_t value, const uint64_t start, const uint64_t length)
|
||||
{
|
||||
return is_within_start_and_end(value, start, start + length);
|
||||
return is_within_start_and_end(value, start, start + length);
|
||||
}
|
||||
|
||||
constexpr bool regions_intersect(const uint64_t start1, const uint64_t end1, const uint64_t start2, const uint64_t end2)
|
||||
{
|
||||
return start1 < end2 && start2 < end1;
|
||||
return start1 < end2 && start2 < end1;
|
||||
}
|
||||
|
||||
constexpr bool regions_with_length_intersect(const uint64_t start1, const uint64_t length1, const uint64_t start2,
|
||||
const uint64_t length2)
|
||||
{
|
||||
return regions_intersect(start1, start1 + length1, start2, start2 + length2);
|
||||
return regions_intersect(start1, start1 + length1, start2, start2 + length2);
|
||||
}
|
||||
|
||||
constexpr uint64_t align_down(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return value & ~(alignment - 1);
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
constexpr uint64_t align_up(const uint64_t value, const uint64_t alignment)
|
||||
{
|
||||
return align_down(value + (alignment - 1), alignment);
|
||||
return align_down(value + (alignment - 1), alignment);
|
||||
}
|
||||
|
||||
constexpr uint64_t page_align_down(const uint64_t value, const uint64_t page_size = 0x1000)
|
||||
{
|
||||
return align_down(value, page_size);
|
||||
return align_down(value, page_size);
|
||||
}
|
||||
|
||||
constexpr uint64_t page_align_up(const uint64_t value, const uint64_t page_size = 0x1000)
|
||||
{
|
||||
return align_up(value, page_size);
|
||||
return align_up(value, page_size);
|
||||
}
|
||||
|
||||
@@ -11,156 +11,152 @@ using memory_operation = memory_permission;
|
||||
|
||||
enum class instruction_hook_continuation : bool
|
||||
{
|
||||
run_instruction = false,
|
||||
skip_instruction = true,
|
||||
run_instruction = false,
|
||||
skip_instruction = true,
|
||||
};
|
||||
|
||||
enum class memory_violation_continuation : bool
|
||||
{
|
||||
stop = false,
|
||||
resume = true,
|
||||
stop = false,
|
||||
resume = true,
|
||||
};
|
||||
|
||||
enum class memory_violation_type : uint8_t
|
||||
{
|
||||
unmapped,
|
||||
protection,
|
||||
unmapped,
|
||||
protection,
|
||||
};
|
||||
|
||||
struct basic_block
|
||||
{
|
||||
uint64_t address;
|
||||
size_t instruction_count;
|
||||
size_t size;
|
||||
uint64_t address;
|
||||
size_t instruction_count;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
using edge_generation_hook_callback = std::function<void(const basic_block& current_block,
|
||||
const basic_block& previous_block)>;
|
||||
using edge_generation_hook_callback =
|
||||
std::function<void(const basic_block& current_block, const basic_block& previous_block)>;
|
||||
using basic_block_hook_callback = std::function<void(const basic_block& block)>;
|
||||
|
||||
using instruction_hook_callback = std::function<instruction_hook_continuation()>;
|
||||
|
||||
using interrupt_hook_callback = std::function<void(int interrupt)>;
|
||||
using simple_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value)>;
|
||||
using complex_memory_hook_callback = std::function<void(uint64_t address, size_t size, uint64_t value,
|
||||
memory_operation operation)>;
|
||||
using complex_memory_hook_callback =
|
||||
std::function<void(uint64_t address, size_t size, uint64_t value, memory_operation operation)>;
|
||||
using memory_violation_hook_callback = std::function<memory_violation_continuation(
|
||||
uint64_t address, size_t size, memory_operation operation,
|
||||
memory_violation_type type)>;
|
||||
uint64_t address, size_t size, memory_operation operation, memory_violation_type type)>;
|
||||
|
||||
class emulator : public memory_manager
|
||||
{
|
||||
public:
|
||||
emulator() = default;
|
||||
public:
|
||||
emulator() = default;
|
||||
|
||||
emulator(const emulator&) = delete;
|
||||
emulator& operator=(const emulator&) = delete;
|
||||
emulator(const emulator&) = delete;
|
||||
emulator& operator=(const emulator&) = delete;
|
||||
|
||||
emulator(emulator&&) = delete;
|
||||
emulator& operator=(emulator&&) = delete;
|
||||
emulator(emulator&&) = delete;
|
||||
emulator& operator=(emulator&&) = delete;
|
||||
|
||||
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void read_raw_register(int reg, void* value, size_t size) = 0;
|
||||
virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
|
||||
virtual void read_raw_register(int reg, void* value, size_t size) = 0;
|
||||
virtual void write_raw_register(int reg, const void* value, size_t size) = 0;
|
||||
|
||||
virtual std::vector<std::byte> save_registers() = 0;
|
||||
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
||||
virtual std::vector<std::byte> save_registers() = 0;
|
||||
virtual void restore_registers(const std::vector<std::byte>& register_data) = 0;
|
||||
|
||||
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
|
||||
memory_violation_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_memory_violation(uint64_t address, size_t size,
|
||||
memory_violation_hook_callback callback) = 0;
|
||||
|
||||
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
|
||||
complex_memory_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_memory_access(uint64_t address, size_t size, memory_operation filter,
|
||||
complex_memory_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0;
|
||||
|
||||
virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_interrupt(interrupt_hook_callback callback) = 0;
|
||||
|
||||
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_edge_generation(edge_generation_hook_callback callback) = 0;
|
||||
virtual emulator_hook* hook_basic_block(basic_block_hook_callback callback) = 0;
|
||||
|
||||
virtual void delete_hook(emulator_hook* hook) = 0;
|
||||
virtual void delete_hook(emulator_hook* hook) = 0;
|
||||
|
||||
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback)
|
||||
{
|
||||
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback));
|
||||
}
|
||||
emulator_hook* hook_memory_violation(memory_violation_hook_callback callback)
|
||||
{
|
||||
return this->hook_memory_violation(0, std::numeric_limits<size_t>::max(), std::move(callback));
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
||||
{
|
||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read);
|
||||
}
|
||||
emulator_hook* hook_memory_read(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
||||
{
|
||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::read);
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
||||
{
|
||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write);
|
||||
}
|
||||
emulator_hook* hook_memory_write(const uint64_t address, const size_t size, simple_memory_hook_callback callback)
|
||||
{
|
||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::write);
|
||||
}
|
||||
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, const size_t size,
|
||||
simple_memory_hook_callback callback)
|
||||
{
|
||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec);
|
||||
}
|
||||
emulator_hook* hook_memory_execution(const uint64_t address, const size_t size,
|
||||
simple_memory_hook_callback callback)
|
||||
{
|
||||
return this->hook_simple_memory_access(address, size, std::move(callback), memory_operation::exec);
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
this->perform_serialization(buffer, false);
|
||||
}
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
this->perform_serialization(buffer, false);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
this->perform_deserialization(buffer, false);
|
||||
}
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
this->perform_deserialization(buffer, false);
|
||||
}
|
||||
|
||||
void save_snapshot()
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
this->perform_serialization(serializer, true);
|
||||
this->last_snapshot_data_ = serializer.move_buffer();
|
||||
}
|
||||
void save_snapshot()
|
||||
{
|
||||
utils::buffer_serializer serializer{};
|
||||
this->perform_serialization(serializer, true);
|
||||
this->last_snapshot_data_ = serializer.move_buffer();
|
||||
}
|
||||
|
||||
void restore_snapshot()
|
||||
{
|
||||
if (this->last_snapshot_data_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
void restore_snapshot()
|
||||
{
|
||||
if (this->last_snapshot_data_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::buffer_deserializer deserializer{this->last_snapshot_data_};
|
||||
this->perform_deserialization(deserializer, true);
|
||||
}
|
||||
utils::buffer_deserializer deserializer{this->last_snapshot_data_};
|
||||
this->perform_deserialization(deserializer, true);
|
||||
}
|
||||
|
||||
virtual bool has_violation() const = 0;
|
||||
virtual bool has_violation() const = 0;
|
||||
|
||||
private:
|
||||
std::vector<std::byte> last_snapshot_data_{};
|
||||
private:
|
||||
std::vector<std::byte> last_snapshot_data_{};
|
||||
|
||||
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
|
||||
simple_memory_hook_callback callback, const memory_operation operation)
|
||||
{
|
||||
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
|
||||
return this->hook_memory_access(address, size, operation,
|
||||
[c = std::move(callback)](const uint64_t a, const size_t s,
|
||||
const uint64_t value,
|
||||
memory_operation)
|
||||
{
|
||||
c(a, s, value);
|
||||
});
|
||||
}
|
||||
emulator_hook* hook_simple_memory_access(const uint64_t address, const size_t size,
|
||||
simple_memory_hook_callback callback, const memory_operation operation)
|
||||
{
|
||||
assert((static_cast<uint8_t>(operation) & (static_cast<uint8_t>(operation) - 1)) == 0);
|
||||
return this->hook_memory_access(address, size, operation,
|
||||
[c = std::move(callback)](const uint64_t a, const size_t s,
|
||||
const uint64_t value,
|
||||
memory_operation) { c(a, s, value); });
|
||||
}
|
||||
|
||||
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||
{
|
||||
this->serialize_state(buffer, is_snapshot);
|
||||
this->serialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
void perform_serialization(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||
{
|
||||
this->serialize_state(buffer, is_snapshot);
|
||||
this->serialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
|
||||
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||
{
|
||||
this->deserialize_state(buffer, is_snapshot);
|
||||
this->deserialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
void perform_deserialization(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||
{
|
||||
this->deserialize_state(buffer, is_snapshot);
|
||||
this->deserialize_memory_state(buffer, is_snapshot);
|
||||
}
|
||||
|
||||
virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0;
|
||||
virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0;
|
||||
virtual void serialize_state(utils::buffer_serializer& buffer, bool is_snapshot) const = 0;
|
||||
virtual void deserialize_state(utils::buffer_deserializer& buffer, bool is_snapshot) = 0;
|
||||
};
|
||||
|
||||
@@ -10,541 +10,547 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
||||
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
||||
constexpr auto MIN_ALLOCATION_ADDRESS = 0x0000000000010000ULL;
|
||||
constexpr auto MAX_ALLOCATION_ADDRESS = 0x00007ffffffeffffULL;
|
||||
|
||||
void split_regions(memory_manager::committed_region_map& regions, const std::vector<uint64_t>& split_points)
|
||||
{
|
||||
for (auto i = regions.begin(); i != regions.end(); ++i)
|
||||
{
|
||||
for (const auto split_point : split_points)
|
||||
{
|
||||
if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
|
||||
{
|
||||
const auto first_length = split_point - i->first;
|
||||
const auto second_length = i->second.length - first_length;
|
||||
void split_regions(memory_manager::committed_region_map& regions, const std::vector<uint64_t>& split_points)
|
||||
{
|
||||
for (auto i = regions.begin(); i != regions.end(); ++i)
|
||||
{
|
||||
for (const auto split_point : split_points)
|
||||
{
|
||||
if (is_within_start_and_length(split_point, i->first, i->second.length) && i->first != split_point)
|
||||
{
|
||||
const auto first_length = split_point - i->first;
|
||||
const auto second_length = i->second.length - first_length;
|
||||
|
||||
i->second.length = first_length;
|
||||
i->second.length = first_length;
|
||||
|
||||
regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
regions[split_point] = memory_manager::committed_region{second_length, i->second.pemissions};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void merge_regions(memory_manager::committed_region_map& regions)
|
||||
{
|
||||
for (auto i = regions.begin(); i != regions.end();)
|
||||
{
|
||||
assert(i->second.length > 0);
|
||||
void merge_regions(memory_manager::committed_region_map& regions)
|
||||
{
|
||||
for (auto i = regions.begin(); i != regions.end();)
|
||||
{
|
||||
assert(i->second.length > 0);
|
||||
|
||||
auto next = i;
|
||||
std::advance(next, 1);
|
||||
auto next = i;
|
||||
std::advance(next, 1);
|
||||
|
||||
if (next == regions.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (next == regions.end())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
assert(next->second.length > 0);
|
||||
assert(next->second.length > 0);
|
||||
|
||||
const auto end = i->first + i->second.length;
|
||||
assert(end <= next->first);
|
||||
const auto end = i->first + i->second.length;
|
||||
assert(end <= next->first);
|
||||
|
||||
if (end != next->first || i->second.pemissions != next->second.pemissions)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
if (end != next->first || i->second.pemissions != next->second.pemissions)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->second.length += next->second.length;
|
||||
regions.erase(next);
|
||||
}
|
||||
}
|
||||
i->second.length += next->second.length;
|
||||
regions.erase(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace utils
|
||||
{
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
|
||||
{
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write(region.pemissions);
|
||||
}
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::committed_region& region)
|
||||
{
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write(region.pemissions);
|
||||
}
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.pemissions = buffer.read<memory_permission>();
|
||||
}
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::committed_region& region)
|
||||
{
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
region.pemissions = buffer.read<memory_permission>();
|
||||
}
|
||||
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write(region.is_mmio);
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
}
|
||||
static void serialize(buffer_serializer& buffer, const memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.write(region.is_mmio);
|
||||
buffer.write<uint64_t>(region.length);
|
||||
buffer.write_map(region.committed_regions);
|
||||
}
|
||||
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.read(region.is_mmio);
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
buffer.read_map(region.committed_regions);
|
||||
}
|
||||
static void deserialize(buffer_deserializer& buffer, memory_manager::reserved_region& region)
|
||||
{
|
||||
buffer.read(region.is_mmio);
|
||||
region.length = static_cast<size_t>(buffer.read<uint64_t>());
|
||||
buffer.read_map(region.committed_regions);
|
||||
}
|
||||
}
|
||||
|
||||
void memory_manager::serialize_memory_state(utils::buffer_serializer& buffer, const bool is_snapshot) const
|
||||
{
|
||||
buffer.write_map(this->reserved_regions_);
|
||||
buffer.write_map(this->reserved_regions_);
|
||||
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<uint8_t> data{};
|
||||
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
if (reserved_region.second.is_mmio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
if (reserved_region.second.is_mmio)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
data.resize(region.second.length);
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
data.resize(region.second.length);
|
||||
|
||||
this->read_memory(region.first, data.data(), region.second.length);
|
||||
this->read_memory(region.first, data.data(), region.second.length);
|
||||
|
||||
buffer.write(data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
buffer.write(data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void memory_manager::deserialize_memory_state(utils::buffer_deserializer& buffer, const bool is_snapshot)
|
||||
{
|
||||
if (!is_snapshot)
|
||||
{
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
this->unmap_memory(region.first, region.second.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_snapshot)
|
||||
{
|
||||
for (const auto& reserved_region : this->reserved_regions_)
|
||||
{
|
||||
for (const auto& region : reserved_region.second.committed_regions)
|
||||
{
|
||||
this->unmap_memory(region.first, region.second.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.read_map(this->reserved_regions_);
|
||||
buffer.read_map(this->reserved_regions_);
|
||||
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (is_snapshot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<uint8_t> data{};
|
||||
|
||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||
{
|
||||
auto& reserved_region = i->second;
|
||||
if (reserved_region.is_mmio)
|
||||
{
|
||||
i = this->reserved_regions_.erase(i);
|
||||
continue;
|
||||
}
|
||||
for (auto i = this->reserved_regions_.begin(); i != this->reserved_regions_.end();)
|
||||
{
|
||||
auto& reserved_region = i->second;
|
||||
if (reserved_region.is_mmio)
|
||||
{
|
||||
i = this->reserved_regions_.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
++i;
|
||||
|
||||
for (const auto& region : reserved_region.committed_regions)
|
||||
{
|
||||
data.resize(region.second.length);
|
||||
for (const auto& region : reserved_region.committed_regions)
|
||||
{
|
||||
data.resize(region.second.length);
|
||||
|
||||
buffer.read(data.data(), region.second.length);
|
||||
buffer.read(data.data(), region.second.length);
|
||||
|
||||
this->map_memory(region.first, region.second.length, region.second.pemissions);
|
||||
this->write_memory(region.first, data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
this->map_memory(region.first, region.second.length, region.second.pemissions);
|
||||
this->write_memory(region.first, data.data(), region.second.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool memory_manager::protect_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||
memory_permission* old_permissions)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region protect not supported yet!");
|
||||
}
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region protect not supported yet!");
|
||||
}
|
||||
|
||||
std::optional<memory_permission> old_first_permissions{};
|
||||
std::optional<memory_permission> old_first_permissions{};
|
||||
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
if (sub_region.first >= address && sub_region_end <= end)
|
||||
{
|
||||
if (!old_first_permissions.has_value())
|
||||
{
|
||||
old_first_permissions = sub_region.second.pemissions;
|
||||
}
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
if (sub_region.first >= address && sub_region_end <= end)
|
||||
{
|
||||
if (!old_first_permissions.has_value())
|
||||
{
|
||||
old_first_permissions = sub_region.second.pemissions;
|
||||
}
|
||||
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||
sub_region.second.pemissions = permissions;
|
||||
}
|
||||
}
|
||||
this->apply_memory_protection(sub_region.first, sub_region.second.length, permissions);
|
||||
sub_region.second.pemissions = permissions;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_permissions)
|
||||
{
|
||||
*old_permissions = old_first_permissions.value_or(memory_permission::none);
|
||||
}
|
||||
if (old_permissions)
|
||||
{
|
||||
*old_permissions = old_first_permissions.value_or(memory_permission::none);
|
||||
}
|
||||
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_mmio(const uint64_t address, const size_t size, mmio_read_callback read_cb,
|
||||
mmio_write_callback write_cb)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
|
||||
this->map_mmio(address, size, std::move(read_cb), std::move(write_cb));
|
||||
|
||||
const auto entry = this->reserved_regions_.try_emplace(address,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.is_mmio = true,
|
||||
}).first;
|
||||
const auto entry = this->reserved_regions_
|
||||
.try_emplace(address,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
.is_mmio = true,
|
||||
})
|
||||
.first;
|
||||
|
||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::allocate_memory(const uint64_t address, const size_t size, const memory_permission permissions,
|
||||
const bool reserve_only)
|
||||
{
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this->overlaps_reserved_region(address, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto entry = this->reserved_regions_.try_emplace(address, reserved_region{.length = size,}).first;
|
||||
const auto entry = this->reserved_regions_
|
||||
.try_emplace(address,
|
||||
reserved_region{
|
||||
.length = size,
|
||||
})
|
||||
.first;
|
||||
|
||||
if (!reserve_only)
|
||||
{
|
||||
this->map_memory(address, size, permissions);
|
||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||
}
|
||||
if (!reserve_only)
|
||||
{
|
||||
this->map_memory(address, size, permissions);
|
||||
entry->second.committed_regions[address] = committed_region{size, memory_permission::read_write};
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::commit_memory(const uint64_t address, const size_t size, const memory_permission permissions)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region commit not supported yet!");
|
||||
}
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region commit not supported yet!");
|
||||
}
|
||||
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
uint64_t last_region_start{};
|
||||
const committed_region* last_region{nullptr};
|
||||
uint64_t last_region_start{};
|
||||
const committed_region* last_region{nullptr};
|
||||
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto& sub_region : committed_regions)
|
||||
{
|
||||
if (sub_region.first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
if (sub_region.first >= address && sub_region_end <= end)
|
||||
{
|
||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||
const auto map_length = sub_region.first - map_start;
|
||||
const auto sub_region_end = sub_region.first + sub_region.second.length;
|
||||
if (sub_region.first >= address && sub_region_end <= end)
|
||||
{
|
||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||
const auto map_length = sub_region.first - map_start;
|
||||
|
||||
if (map_length > 0)
|
||||
{
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
if (map_length > 0)
|
||||
{
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
|
||||
last_region_start = sub_region.first;
|
||||
last_region = &sub_region.second;
|
||||
}
|
||||
}
|
||||
last_region_start = sub_region.first;
|
||||
last_region = &sub_region.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!last_region || (last_region_start + last_region->length) < end)
|
||||
{
|
||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||
const auto map_length = end - map_start;
|
||||
if (!last_region || (last_region_start + last_region->length) < end)
|
||||
{
|
||||
const auto map_start = last_region ? (last_region_start + last_region->length) : address;
|
||||
const auto map_length = end - map_start;
|
||||
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
this->map_memory(map_start, map_length, permissions);
|
||||
committed_regions[map_start] = committed_region{map_length, permissions};
|
||||
}
|
||||
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
merge_regions(committed_regions);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::decommit_memory(const uint64_t address, const size_t size)
|
||||
{
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->find_reserved_region(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry->second.is_mmio)
|
||||
{
|
||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||
}
|
||||
if (entry->second.is_mmio)
|
||||
{
|
||||
throw std::runtime_error("Not allowed to decommit MMIO!");
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
const auto end = address + size;
|
||||
const auto region_end = entry->first + entry->second.length;
|
||||
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region decommit not supported yet!");
|
||||
}
|
||||
if (region_end < end)
|
||||
{
|
||||
throw std::runtime_error("Cross region decommit not supported yet!");
|
||||
}
|
||||
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
|
||||
split_regions(committed_regions, {address, end});
|
||||
split_regions(committed_regions, {address, end});
|
||||
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
continue;
|
||||
}
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool memory_manager::release_memory(const uint64_t address, size_t size)
|
||||
{
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto entry = this->reserved_regions_.find(address);
|
||||
if (entry == this->reserved_regions_.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
{
|
||||
size = entry->second.length;
|
||||
}
|
||||
if (!size)
|
||||
{
|
||||
size = entry->second.length;
|
||||
}
|
||||
|
||||
if (size > entry->second.length)
|
||||
{
|
||||
throw std::runtime_error("Cross region release not supported yet!");
|
||||
}
|
||||
if (size > entry->second.length)
|
||||
{
|
||||
throw std::runtime_error("Cross region release not supported yet!");
|
||||
}
|
||||
|
||||
const auto end = address + size;
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
const auto end = address + size;
|
||||
auto& committed_regions = entry->second.committed_regions;
|
||||
|
||||
split_regions(committed_regions, {end});
|
||||
split_regions(committed_regions, {end});
|
||||
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
for (auto i = committed_regions.begin(); i != committed_regions.end();)
|
||||
{
|
||||
if (i->first >= end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
const auto sub_region_end = i->first + i->second.length;
|
||||
if (i->first >= address && sub_region_end <= end)
|
||||
{
|
||||
this->unmap_memory(i->first, i->second.length);
|
||||
i = committed_regions.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
entry->second.length -= size;
|
||||
if (entry->second.length > 0)
|
||||
{
|
||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||
}
|
||||
entry->second.length -= size;
|
||||
if (entry->second.length > 0)
|
||||
{
|
||||
this->reserved_regions_[address + size] = std::move(entry->second);
|
||||
}
|
||||
|
||||
this->reserved_regions_.erase(entry);
|
||||
return true;
|
||||
this->reserved_regions_.erase(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t memory_manager::find_free_allocation_base(const size_t size, const uint64_t start) const
|
||||
{
|
||||
uint64_t start_address =
|
||||
std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
|
||||
uint64_t start_address = std::max(MIN_ALLOCATION_ADDRESS, start ? start : 0x100000000ULL);
|
||||
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
const auto region_end = region.first + region.second.length;
|
||||
if (region_end < start_address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
const auto region_end = region.first + region.second.length;
|
||||
if (region_end < start_address)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
if (!regions_with_length_intersect(start_address, size, region.first, region.second.length))
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
|
||||
start_address = page_align_up(region_end);
|
||||
}
|
||||
start_address = page_align_up(region_end);
|
||||
}
|
||||
|
||||
if (start_address + size <= MAX_ALLOCATION_ADDRESS)
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
if (start_address + size <= MAX_ALLOCATION_ADDRESS)
|
||||
{
|
||||
return start_address;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
region_info memory_manager::get_region_info(const uint64_t address)
|
||||
{
|
||||
region_info result{};
|
||||
result.start = MIN_ALLOCATION_ADDRESS;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
result.permissions = memory_permission::none;
|
||||
result.allocation_base = {};
|
||||
result.allocation_length = result.length;
|
||||
result.is_committed = false;
|
||||
result.is_reserved = false;
|
||||
region_info result{};
|
||||
result.start = MIN_ALLOCATION_ADDRESS;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
result.permissions = memory_permission::none;
|
||||
result.allocation_base = {};
|
||||
result.allocation_length = result.length;
|
||||
result.is_committed = false;
|
||||
result.is_reserved = false;
|
||||
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
result.length = upper_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
result.length = upper_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto entry = --upper_bound;
|
||||
const auto lower_end = entry->first + entry->second.length;
|
||||
if (lower_end <= address)
|
||||
{
|
||||
result.start = lower_end;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
return result;
|
||||
}
|
||||
const auto entry = --upper_bound;
|
||||
const auto lower_end = entry->first + entry->second.length;
|
||||
if (lower_end <= address)
|
||||
{
|
||||
result.start = lower_end;
|
||||
result.length = MAX_ALLOCATION_ADDRESS - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
// We have a reserved region
|
||||
const auto& reserved_region = entry->second;
|
||||
const auto& committed_regions = reserved_region.committed_regions;
|
||||
// We have a reserved region
|
||||
const auto& reserved_region = entry->second;
|
||||
const auto& committed_regions = reserved_region.committed_regions;
|
||||
|
||||
result.is_reserved = true;
|
||||
result.allocation_base = entry->first;
|
||||
result.allocation_length = reserved_region.length;
|
||||
result.start = result.allocation_base;
|
||||
result.length = result.allocation_length;
|
||||
result.is_reserved = true;
|
||||
result.allocation_base = entry->first;
|
||||
result.allocation_length = reserved_region.length;
|
||||
result.start = result.allocation_base;
|
||||
result.length = result.allocation_length;
|
||||
|
||||
if (committed_regions.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (committed_regions.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto committed_bound = committed_regions.upper_bound(address);
|
||||
if (committed_bound == committed_regions.begin())
|
||||
{
|
||||
result.length = committed_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
auto committed_bound = committed_regions.upper_bound(address);
|
||||
if (committed_bound == committed_regions.begin())
|
||||
{
|
||||
result.length = committed_bound->first - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto committed_entry = --committed_bound;
|
||||
const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
|
||||
if (committed_lower_end <= address)
|
||||
{
|
||||
result.start = committed_lower_end;
|
||||
result.length = lower_end - result.start;
|
||||
return result;
|
||||
}
|
||||
const auto committed_entry = --committed_bound;
|
||||
const auto committed_lower_end = committed_entry->first + committed_entry->second.length;
|
||||
if (committed_lower_end <= address)
|
||||
{
|
||||
result.start = committed_lower_end;
|
||||
result.length = lower_end - result.start;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.is_committed = true;
|
||||
result.start = committed_entry->first;
|
||||
result.length = committed_entry->second.length;
|
||||
result.permissions = committed_entry->second.pemissions;
|
||||
result.is_committed = true;
|
||||
result.start = committed_entry->first;
|
||||
result.length = committed_entry->second.length;
|
||||
result.permissions = committed_entry->second.pemissions;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
memory_manager::reserved_region_map::iterator memory_manager::find_reserved_region(const uint64_t address)
|
||||
{
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
if (this->reserved_regions_.empty())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
auto upper_bound = this->reserved_regions_.upper_bound(address);
|
||||
if (upper_bound == this->reserved_regions_.begin())
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
|
||||
const auto entry = --upper_bound;
|
||||
if (entry->first + entry->second.length <= address)
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
const auto entry = --upper_bound;
|
||||
if (entry->first + entry->second.length <= address)
|
||||
{
|
||||
return this->reserved_regions_.end();
|
||||
}
|
||||
|
||||
return entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
bool memory_manager::overlaps_reserved_region(const uint64_t address, const size_t size) const
|
||||
{
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
if (regions_with_length_intersect(address, size, region.first, region.second.length))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (const auto& region : this->reserved_regions_)
|
||||
{
|
||||
if (regions_with_length_intersect(address, size, region.first, region.second.length))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
struct region_info : basic_memory_region
|
||||
{
|
||||
uint64_t allocation_base{};
|
||||
size_t allocation_length{};
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
uint64_t allocation_base{};
|
||||
size_t allocation_length{};
|
||||
bool is_reserved{};
|
||||
bool is_committed{};
|
||||
};
|
||||
|
||||
using mmio_read_callback = std::function<uint64_t(uint64_t addr, size_t size)>;
|
||||
@@ -18,115 +18,114 @@ using mmio_write_callback = std::function<void(uint64_t addr, size_t size, uint6
|
||||
|
||||
class memory_manager
|
||||
{
|
||||
public:
|
||||
struct committed_region
|
||||
{
|
||||
size_t length{};
|
||||
memory_permission pemissions{};
|
||||
};
|
||||
public:
|
||||
struct committed_region
|
||||
{
|
||||
size_t length{};
|
||||
memory_permission pemissions{};
|
||||
};
|
||||
|
||||
using committed_region_map = std::map<uint64_t, committed_region>;
|
||||
using committed_region_map = std::map<uint64_t, committed_region>;
|
||||
|
||||
struct reserved_region
|
||||
{
|
||||
size_t length{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
};
|
||||
struct reserved_region
|
||||
{
|
||||
size_t length{};
|
||||
committed_region_map committed_regions{};
|
||||
bool is_mmio{false};
|
||||
};
|
||||
|
||||
virtual ~memory_manager() = default;
|
||||
virtual ~memory_manager() = default;
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const uint64_t address) const
|
||||
{
|
||||
T value{};
|
||||
this->read_memory(address, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
template <typename T>
|
||||
T read_memory(const uint64_t address) const
|
||||
{
|
||||
T value{};
|
||||
this->read_memory(address, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read_memory(const void* address) const
|
||||
{
|
||||
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
||||
}
|
||||
template <typename T>
|
||||
T read_memory(const void* address) const
|
||||
{
|
||||
return this->read_memory<T>(reinterpret_cast<uint64_t>(address));
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(size);
|
||||
std::vector<std::byte> read_memory(const uint64_t address, const size_t size) const
|
||||
{
|
||||
std::vector<std::byte> data{};
|
||||
data.resize(size);
|
||||
|
||||
this->read_memory(address, data.data(), data.size());
|
||||
this->read_memory(address, data.data(), data.size());
|
||||
|
||||
return data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
||||
{
|
||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||
}
|
||||
std::vector<std::byte> read_memory(const void* address, const size_t size) const
|
||||
{
|
||||
return this->read_memory(reinterpret_cast<uint64_t>(address), size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(const uint64_t address, const T& value)
|
||||
{
|
||||
this->write_memory(address, &value, sizeof(value));
|
||||
}
|
||||
template <typename T>
|
||||
void write_memory(const uint64_t address, const T& value)
|
||||
{
|
||||
this->write_memory(address, &value, sizeof(value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_memory(void* address, const T& value)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||
}
|
||||
template <typename T>
|
||||
void write_memory(void* address, const T& value)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), &value, sizeof(value));
|
||||
}
|
||||
|
||||
void write_memory(void* address, const void* data, const size_t size)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
|
||||
}
|
||||
void write_memory(void* address, const void* data, const size_t size)
|
||||
{
|
||||
this->write_memory(reinterpret_cast<uint64_t>(address), data, size);
|
||||
}
|
||||
|
||||
virtual void read_memory(uint64_t address, void* data, size_t size) const = 0;
|
||||
virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0;
|
||||
virtual void write_memory(uint64_t address, const void* data, size_t size) = 0;
|
||||
virtual void read_memory(uint64_t address, void* data, size_t size) const = 0;
|
||||
virtual bool try_read_memory(uint64_t address, void* data, size_t size) const = 0;
|
||||
virtual void write_memory(uint64_t address, const void* data, size_t size) = 0;
|
||||
|
||||
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
memory_permission* old_permissions = nullptr);
|
||||
bool protect_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
memory_permission* old_permissions = nullptr);
|
||||
|
||||
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
|
||||
bool allocate_memory(uint64_t address, size_t size, memory_permission permissions,
|
||||
bool reserve_only = false);
|
||||
bool allocate_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb);
|
||||
bool allocate_memory(uint64_t address, size_t size, memory_permission permissions, bool reserve_only = false);
|
||||
|
||||
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
||||
bool decommit_memory(uint64_t address, size_t size);
|
||||
bool commit_memory(uint64_t address, size_t size, memory_permission permissions);
|
||||
bool decommit_memory(uint64_t address, size_t size);
|
||||
|
||||
bool release_memory(uint64_t address, size_t size);
|
||||
bool release_memory(uint64_t address, size_t size);
|
||||
|
||||
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
||||
uint64_t find_free_allocation_base(size_t size, uint64_t start = 0) const;
|
||||
|
||||
region_info get_region_info(uint64_t address);
|
||||
region_info get_region_info(uint64_t address);
|
||||
|
||||
uint64_t allocate_memory(const size_t size, const memory_permission permissions, const bool reserve_only = false)
|
||||
{
|
||||
const auto allocation_base = this->find_free_allocation_base(size);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
uint64_t allocate_memory(const size_t size, const memory_permission permissions, const bool reserve_only = false)
|
||||
{
|
||||
const auto allocation_base = this->find_free_allocation_base(size);
|
||||
if (!allocate_memory(allocation_base, size, permissions, reserve_only))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return allocation_base;
|
||||
}
|
||||
return allocation_base;
|
||||
}
|
||||
|
||||
private:
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
reserved_region_map reserved_regions_{};
|
||||
private:
|
||||
using reserved_region_map = std::map<uint64_t, reserved_region>;
|
||||
reserved_region_map reserved_regions_{};
|
||||
|
||||
reserved_region_map::iterator find_reserved_region(uint64_t address);
|
||||
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
||||
reserved_region_map::iterator find_reserved_region(uint64_t address);
|
||||
bool overlaps_reserved_region(uint64_t address, size_t size) const;
|
||||
|
||||
virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0;
|
||||
virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
virtual void unmap_memory(uint64_t address, size_t size) = 0;
|
||||
virtual void map_mmio(uint64_t address, size_t size, mmio_read_callback read_cb, mmio_write_callback write_cb) = 0;
|
||||
virtual void map_memory(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
virtual void unmap_memory(uint64_t address, size_t size) = 0;
|
||||
|
||||
virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
virtual void apply_memory_protection(uint64_t address, size_t size, memory_permission permissions) = 0;
|
||||
|
||||
protected:
|
||||
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
|
||||
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
|
||||
protected:
|
||||
void serialize_memory_state(utils::buffer_serializer& buffer, bool is_snapshot) const;
|
||||
void deserialize_memory_state(utils::buffer_deserializer& buffer, bool is_snapshot);
|
||||
};
|
||||
|
||||
@@ -3,62 +3,52 @@
|
||||
|
||||
enum class memory_permission : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
read = 1 << 0,
|
||||
write = 1 << 1,
|
||||
exec = 1 << 2,
|
||||
read_write = read | write,
|
||||
all = read | write | exec
|
||||
none = 0,
|
||||
read = 1 << 0,
|
||||
write = 1 << 1,
|
||||
exec = 1 << 2,
|
||||
read_write = read | write,
|
||||
all = read | write | exec
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator&(const memory_permission x, const memory_permission y)
|
||||
inline constexpr memory_permission operator&(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator|(const memory_permission x, const memory_permission y)
|
||||
inline constexpr memory_permission operator|(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator^(const memory_permission x, const memory_permission y)
|
||||
inline constexpr memory_permission operator^(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>
|
||||
(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission
|
||||
operator~(memory_permission x)
|
||||
inline constexpr memory_permission operator~(memory_permission x)
|
||||
{
|
||||
return static_cast<memory_permission>(~static_cast<uint8_t>(x));
|
||||
return static_cast<memory_permission>(~static_cast<uint8_t>(x));
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator&=(memory_permission& x, const memory_permission y)
|
||||
inline memory_permission& operator&=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x & y;
|
||||
return x;
|
||||
x = x & y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator|=(memory_permission& x, const memory_permission y)
|
||||
inline memory_permission& operator|=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x | y;
|
||||
return x;
|
||||
x = x | y;
|
||||
return x;
|
||||
}
|
||||
|
||||
inline memory_permission&
|
||||
operator^=(memory_permission& x, const memory_permission y)
|
||||
inline memory_permission& operator^=(memory_permission& x, const memory_permission y)
|
||||
{
|
||||
x = x ^ y;
|
||||
return x;
|
||||
x = x ^ y;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
struct basic_memory_region
|
||||
{
|
||||
uint64_t start{};
|
||||
size_t length{};
|
||||
memory_permission permissions{};
|
||||
uint64_t start{};
|
||||
size_t length{};
|
||||
memory_permission permissions{};
|
||||
};
|
||||
|
||||
struct memory_region : basic_memory_region
|
||||
{
|
||||
bool committed{};
|
||||
bool committed{};
|
||||
};
|
||||
|
||||
@@ -3,52 +3,52 @@
|
||||
|
||||
class scoped_hook
|
||||
{
|
||||
public:
|
||||
scoped_hook() = default;
|
||||
public:
|
||||
scoped_hook() = default;
|
||||
|
||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||
: emu_(&emu)
|
||||
, hook_(hook)
|
||||
{
|
||||
}
|
||||
scoped_hook(emulator& emu, emulator_hook* hook)
|
||||
: emu_(&emu),
|
||||
hook_(hook)
|
||||
{
|
||||
}
|
||||
|
||||
~scoped_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
~scoped_hook()
|
||||
{
|
||||
this->remove();
|
||||
}
|
||||
|
||||
scoped_hook(const scoped_hook&) = delete;
|
||||
scoped_hook& operator=(const scoped_hook&) = delete;
|
||||
scoped_hook(const scoped_hook&) = delete;
|
||||
scoped_hook& operator=(const scoped_hook&) = delete;
|
||||
|
||||
scoped_hook(scoped_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
scoped_hook(scoped_hook&& obj) noexcept
|
||||
{
|
||||
this->operator=(std::move(obj));
|
||||
}
|
||||
|
||||
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
this->emu_ = obj.emu_;
|
||||
this->hook_ = obj.hook_;
|
||||
scoped_hook& operator=(scoped_hook&& obj) noexcept
|
||||
{
|
||||
if (this != &obj)
|
||||
{
|
||||
this->remove();
|
||||
this->emu_ = obj.emu_;
|
||||
this->hook_ = obj.hook_;
|
||||
|
||||
obj.hook_ = {};
|
||||
}
|
||||
obj.hook_ = {};
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
this->emu_->delete_hook(this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
void remove()
|
||||
{
|
||||
if (this->hook_)
|
||||
{
|
||||
this->emu_->delete_hook(this->hook_);
|
||||
this->hook_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
emulator* emu_{};
|
||||
emulator_hook* hook_{};
|
||||
private:
|
||||
emulator* emu_{};
|
||||
emulator_hook* hook_{};
|
||||
};
|
||||
|
||||
@@ -11,488 +11,483 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class buffer_serializer;
|
||||
class buffer_deserializer;
|
||||
class buffer_serializer;
|
||||
class buffer_deserializer;
|
||||
|
||||
template <typename T>
|
||||
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer)
|
||||
{
|
||||
{ ac.serialize(serializer) } -> std::same_as<void>;
|
||||
{ a.deserialize(deserializer) } -> std::same_as<void>;
|
||||
};
|
||||
template <typename T>
|
||||
concept Serializable = requires(T a, const T ac, buffer_serializer& serializer, buffer_deserializer& deserializer) {
|
||||
{ ac.serialize(serializer) } -> std::same_as<void>;
|
||||
{ a.deserialize(deserializer) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
/* Use concept instead, to prevent overhead of virtual function calls
|
||||
struct serializable
|
||||
{
|
||||
virtual ~serializable() = default;
|
||||
virtual void serialize(buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
||||
};
|
||||
*/
|
||||
/* Use concept instead, to prevent overhead of virtual function calls
|
||||
struct serializable
|
||||
{
|
||||
virtual ~serializable() = default;
|
||||
virtual void serialize(buffer_serializer& buffer) const = 0;
|
||||
virtual void deserialize(buffer_deserializer& buffer) = 0;
|
||||
};
|
||||
*/
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename, typename = void>
|
||||
struct has_serialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
namespace detail
|
||||
{
|
||||
template <typename, typename = void>
|
||||
struct has_serialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_serialize_function<T, std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
||||
std::declval<const std::remove_cvref_t<T>&>())
|
||||
)>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
template <typename T>
|
||||
struct has_serialize_function<T,
|
||||
std::void_t<decltype(serialize(std::declval<buffer_serializer&>(),
|
||||
std::declval<const std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename, typename = void>
|
||||
struct has_deserialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
template <typename, typename = void>
|
||||
struct has_deserialize_function : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(
|
||||
std::declval<buffer_deserializer&>(),
|
||||
std::declval<std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
template <typename T>
|
||||
struct has_deserialize_function<T, std::void_t<decltype(deserialize(std::declval<buffer_deserializer&>(),
|
||||
std::declval<std::remove_cvref_t<T>&>()))>>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_deserializer_constructor
|
||||
: std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
|
||||
{
|
||||
};
|
||||
}
|
||||
template <typename T>
|
||||
struct has_deserializer_constructor : std::bool_constant<std::is_constructible_v<T, buffer_deserializer&>>
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
class buffer_deserializer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
|
||||
: no_debugging_(no_debugging)
|
||||
, buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
}
|
||||
class buffer_deserializer
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::span<T> buffer, bool no_debugging = false)
|
||||
: no_debugging_(no_debugging),
|
||||
buffer_(reinterpret_cast<const std::byte*>(buffer.data()), buffer.size() * sizeof(T))
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
|
||||
: buffer_deserializer(std::span(buffer), no_debugging)
|
||||
{
|
||||
}
|
||||
template <typename T>
|
||||
buffer_deserializer(const std::vector<T>& buffer, bool no_debugging = false)
|
||||
: buffer_deserializer(std::span(buffer), no_debugging)
|
||||
{
|
||||
}
|
||||
|
||||
std::span<const std::byte> read_data(const size_t length)
|
||||
{
|
||||
std::span<const std::byte> read_data(const size_t length)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const uint64_t real_old_size = this->offset_;
|
||||
(void)real_old_size;
|
||||
const uint64_t real_old_size = this->offset_;
|
||||
(void)real_old_size;
|
||||
#endif
|
||||
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
const std::span result(this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
|
||||
(void)this->no_debugging_;
|
||||
(void)this->no_debugging_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!this->no_debugging_)
|
||||
{
|
||||
uint64_t old_size{};
|
||||
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
if (!this->no_debugging_)
|
||||
{
|
||||
uint64_t old_size{};
|
||||
if (this->offset_ + sizeof(old_size) > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
||||
if (old_size != real_old_size)
|
||||
{
|
||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||
}
|
||||
memcpy(&old_size, this->buffer_.data() + this->offset_, sizeof(old_size));
|
||||
if (old_size != real_old_size)
|
||||
{
|
||||
throw std::runtime_error("Reading from serialized buffer mismatches written data!");
|
||||
}
|
||||
|
||||
this->offset_ += sizeof(old_size);
|
||||
}
|
||||
this->offset_ += sizeof(old_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void read(void* data, const size_t length)
|
||||
{
|
||||
const auto span = this->read_data(length);
|
||||
memcpy(data, span.data(), length);
|
||||
}
|
||||
void read(void* data, const size_t length)
|
||||
{
|
||||
const auto span = this->read_data(length);
|
||||
memcpy(data, span.data(), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read(T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
template <typename T>
|
||||
void read(T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.deserialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_deserialize_function<T>::value)
|
||||
{
|
||||
deserialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
T* type_{};
|
||||
void* void_;
|
||||
} pointers;
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.deserialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_deserialize_function<T>::value)
|
||||
{
|
||||
deserialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
T* type_{};
|
||||
void* void_;
|
||||
} pointers;
|
||||
|
||||
pointers.type_ = &object;
|
||||
pointers.type_ = &object;
|
||||
|
||||
this->read(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
this->read(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
auto object = this->construct_object<T>();
|
||||
this->read(object);
|
||||
return object;
|
||||
}
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
auto object = this->construct_object<T>();
|
||||
this->read(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_optional(std::optional<T>& val)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(this->read<T>());
|
||||
}
|
||||
else
|
||||
{
|
||||
val = std::nullopt;
|
||||
}
|
||||
}
|
||||
template <typename T>
|
||||
void read_optional(std::optional<T>& val)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(this->read<T>());
|
||||
}
|
||||
else
|
||||
{
|
||||
val = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void read_optional(std::optional<T>& val, const F& factory)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(factory());
|
||||
this->read<T>(*val);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = {};
|
||||
}
|
||||
}
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void read_optional(std::optional<T>& val, const F& factory)
|
||||
{
|
||||
if (this->read<bool>())
|
||||
{
|
||||
val.emplace(factory());
|
||||
this->read<T>(*val);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = {};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_vector(std::vector<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
template <typename T>
|
||||
void read_vector(std::vector<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.emplace_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.emplace_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
std::vector<T> result{};
|
||||
this->read_vector(result);
|
||||
return result;
|
||||
}
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
std::vector<T> result{};
|
||||
this->read_vector(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void read_map(Map& map)
|
||||
{
|
||||
using key_type = typename Map::key_type;
|
||||
using value_type = typename Map::mapped_type;
|
||||
template <typename Map>
|
||||
void read_map(Map& map)
|
||||
{
|
||||
using key_type = typename Map::key_type;
|
||||
using value_type = typename Map::mapped_type;
|
||||
|
||||
map.clear();
|
||||
map.clear();
|
||||
|
||||
const auto size = this->read<uint64_t>();
|
||||
const auto size = this->read<uint64_t>();
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto key = this->read<key_type>();
|
||||
auto value = this->read<value_type>();
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
auto key = this->read<key_type>();
|
||||
auto value = this->read<value_type>();
|
||||
|
||||
map.emplace(std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
map.emplace(std::move(key), std::move(value));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
Map read_map()
|
||||
{
|
||||
Map map{};
|
||||
this->read_map(map);
|
||||
return map;
|
||||
}
|
||||
template <typename Map>
|
||||
Map read_map()
|
||||
{
|
||||
Map map{};
|
||||
this->read_map(map);
|
||||
return map;
|
||||
}
|
||||
|
||||
template <typename T = char>
|
||||
void read_string(std::basic_string<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
template <typename T = char>
|
||||
void read_string(std::basic_string<T>& result)
|
||||
{
|
||||
const auto size = this->read<uint64_t>();
|
||||
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
result.clear();
|
||||
result.reserve(size);
|
||||
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.push_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
for (uint64_t i = 0; i < size; ++i)
|
||||
{
|
||||
result.push_back(this->read<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T= char>
|
||||
std::basic_string<T> read_string()
|
||||
{
|
||||
std::basic_string<T> result{};
|
||||
this->read_string(result);
|
||||
return result;
|
||||
}
|
||||
template <typename T = char>
|
||||
std::basic_string<T> read_string()
|
||||
{
|
||||
std::basic_string<T> result{};
|
||||
this->read_string(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
|
||||
std::span<const std::byte> get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
std::span<const std::byte> get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
|
||||
size_t get_offset() const
|
||||
{
|
||||
return this->offset_;
|
||||
}
|
||||
size_t get_offset() const
|
||||
{
|
||||
return this->offset_;
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void register_factory(F factory)
|
||||
{
|
||||
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* {
|
||||
return new T(f());
|
||||
};
|
||||
}
|
||||
template <typename T, typename F>
|
||||
requires(std::is_invocable_r_v<T, F>)
|
||||
void register_factory(F factory)
|
||||
{
|
||||
this->factories_[std::type_index(typeid(T))] = [f = std::move(factory)]() -> T* { return new T(f()); };
|
||||
}
|
||||
|
||||
private:
|
||||
bool no_debugging_{false};
|
||||
size_t offset_{0};
|
||||
std::span<const std::byte> buffer_{};
|
||||
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
||||
private:
|
||||
bool no_debugging_{false};
|
||||
size_t offset_{0};
|
||||
std::span<const std::byte> buffer_{};
|
||||
std::unordered_map<std::type_index, std::function<void*()>> factories_{};
|
||||
|
||||
template <typename T>
|
||||
T construct_object()
|
||||
{
|
||||
if constexpr (detail::has_deserializer_constructor<T>::value)
|
||||
{
|
||||
return T(*this);
|
||||
}
|
||||
else if constexpr (std::is_default_constructible_v<T>)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
||||
if (factory == this->factories_.end())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Object construction failed. Missing factory for type: " + std::string(typeid(T).name()));
|
||||
}
|
||||
template <typename T>
|
||||
T construct_object()
|
||||
{
|
||||
if constexpr (detail::has_deserializer_constructor<T>::value)
|
||||
{
|
||||
return T(*this);
|
||||
}
|
||||
else if constexpr (std::is_default_constructible_v<T>)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto factory = this->factories_.find(std::type_index(typeid(T)));
|
||||
if (factory == this->factories_.end())
|
||||
{
|
||||
throw std::runtime_error("Object construction failed. Missing factory for type: " +
|
||||
std::string(typeid(T).name()));
|
||||
}
|
||||
|
||||
auto* object = static_cast<T*>(factory->second());
|
||||
auto obj = std::move(*object);
|
||||
delete object;
|
||||
auto* object = static_cast<T*>(factory->second());
|
||||
auto obj = std::move(*object);
|
||||
delete object;
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class buffer_serializer
|
||||
{
|
||||
public:
|
||||
buffer_serializer() = default;
|
||||
class buffer_serializer
|
||||
{
|
||||
public:
|
||||
buffer_serializer() = default;
|
||||
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
void write(const void* buffer, const size_t length)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const uint64_t old_size = this->buffer_.size();
|
||||
const uint64_t old_size = this->buffer_.size();
|
||||
#endif
|
||||
|
||||
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
||||
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
||||
const auto* byte_buffer = static_cast<const std::byte*>(buffer);
|
||||
this->buffer_.insert(this->buffer_.end(), byte_buffer, byte_buffer + length);
|
||||
|
||||
#ifndef NDEBUG
|
||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
||||
const auto* security_buffer = reinterpret_cast<const std::byte*>(&old_size);
|
||||
this->buffer_.insert(this->buffer_.end(), security_buffer, security_buffer + sizeof(old_size));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void write(const buffer_serializer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
void write(const buffer_serializer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.serialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_serialize_function<T>::value)
|
||||
{
|
||||
serialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
const T* type_{};
|
||||
const void* void_;
|
||||
} pointers;
|
||||
if constexpr (Serializable<T>)
|
||||
{
|
||||
object.serialize(*this);
|
||||
}
|
||||
else if constexpr (detail::has_serialize_function<T>::value)
|
||||
{
|
||||
serialize(*this, object);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
union
|
||||
{
|
||||
const T* type_{};
|
||||
const void* void_;
|
||||
} pointers;
|
||||
|
||||
pointers.type_ = &object;
|
||||
pointers.type_ = &object;
|
||||
|
||||
this->write(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
this->write(pointers.void_, sizeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(!is_trivially_copyable, "Key must be trivially copyable or implement serializable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_optional(const std::optional<T>& val)
|
||||
{
|
||||
this->write(val.has_value());
|
||||
template <typename T>
|
||||
void write_optional(const std::optional<T>& val)
|
||||
{
|
||||
this->write(val.has_value());
|
||||
|
||||
if (val.has_value())
|
||||
{
|
||||
this->write(*val);
|
||||
}
|
||||
}
|
||||
if (val.has_value())
|
||||
{
|
||||
this->write(*val);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_span(const std::span<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
template <typename T>
|
||||
void write_span(const std::span<T> vec)
|
||||
{
|
||||
this->write(static_cast<uint64_t>(vec.size()));
|
||||
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
for (const auto& v : vec)
|
||||
{
|
||||
this->write(v);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T> vec)
|
||||
{
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T> vec)
|
||||
{
|
||||
this->write_span(std::span(vec));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string_view<T> str)
|
||||
{
|
||||
this->write_span<const T>(str);
|
||||
}
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string_view<T> str)
|
||||
{
|
||||
this->write_span<const T>(str);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string<T>& str)
|
||||
{
|
||||
this->write_string(std::basic_string_view<T>(str));
|
||||
}
|
||||
template <typename T>
|
||||
void write_string(const std::basic_string<T>& str)
|
||||
{
|
||||
this->write_string(std::basic_string_view<T>(str));
|
||||
}
|
||||
|
||||
template <typename Map>
|
||||
void write_map(const Map& map)
|
||||
{
|
||||
this->write<uint64_t>(map.size());
|
||||
template <typename Map>
|
||||
void write_map(const Map& map)
|
||||
{
|
||||
this->write<uint64_t>(map.size());
|
||||
|
||||
for (const auto& entry : map)
|
||||
{
|
||||
this->write(entry.first);
|
||||
this->write(entry.second);
|
||||
}
|
||||
}
|
||||
for (const auto& entry : map)
|
||||
{
|
||||
this->write(entry.first);
|
||||
this->write(entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::byte>& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
const std::vector<std::byte>& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::vector<std::byte> move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
std::vector<std::byte> move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::byte> buffer_{};
|
||||
};
|
||||
private:
|
||||
std::vector<std::byte> buffer_{};
|
||||
};
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<bool>(bool& object)
|
||||
{
|
||||
object = this->read<uint8_t>() != 0;
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<bool>(bool& object)
|
||||
{
|
||||
object = this->read<uint8_t>() != 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::string>(std::string& object)
|
||||
{
|
||||
object = this->read_string<char>();
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::string>(std::string& object)
|
||||
{
|
||||
object = this->read_string<char>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
|
||||
{
|
||||
object = this->read_string<wchar_t>();
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::wstring>(std::wstring& object)
|
||||
{
|
||||
object = this->read_string<wchar_t>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
|
||||
{
|
||||
object = this->read_string<char16_t>();
|
||||
}
|
||||
template <>
|
||||
inline void buffer_deserializer::read<std::u16string>(std::u16string& object)
|
||||
{
|
||||
object = this->read_string<char16_t>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<bool>(const bool& object)
|
||||
{
|
||||
this->write<uint8_t>(object ? 1 : 0);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<bool>(const bool& object)
|
||||
{
|
||||
this->write<uint8_t>(object ? 1 : 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::string>(const std::string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::string>(const std::string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::wstring>(const std::wstring& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
template <>
|
||||
inline void buffer_serializer::write<std::u16string>(const std::u16string& object)
|
||||
{
|
||||
this->write_string(object);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,41 +7,41 @@
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::steady_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::steady_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::steady_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
buffer.write(tp.time_since_epoch().count());
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::system_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
inline void deserialize(buffer_deserializer& buffer, std::chrono::system_clock::time_point& tp)
|
||||
{
|
||||
using time_point = std::chrono::system_clock::time_point;
|
||||
using duration = time_point::duration;
|
||||
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
const auto count = buffer.read<duration::rep>();
|
||||
tp = time_point{duration{count}};
|
||||
}
|
||||
|
||||
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
|
||||
{
|
||||
buffer.write_string<char16_t>(path.u16string());
|
||||
}
|
||||
inline void serialize(buffer_serializer& buffer, const std::filesystem::path& path)
|
||||
{
|
||||
buffer.write_string<char16_t>(path.u16string());
|
||||
}
|
||||
|
||||
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
|
||||
{
|
||||
path = buffer.read_string<char16_t>();
|
||||
}
|
||||
inline void deserialize(buffer_deserializer& buffer, std::filesystem::path& path)
|
||||
{
|
||||
path = buffer.read_string<char16_t>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,94 +2,94 @@
|
||||
|
||||
#include "emulator.hpp"
|
||||
|
||||
template <typename PointerType, typename Register, Register InstructionPointer, Register
|
||||
StackPointer, typename HookableInstructions>
|
||||
template <typename PointerType, typename Register, Register InstructionPointer, Register StackPointer,
|
||||
typename HookableInstructions>
|
||||
class typed_emulator : public emulator
|
||||
{
|
||||
public:
|
||||
using registers = Register;
|
||||
using pointer_type = PointerType;
|
||||
using hookable_instructions = HookableInstructions;
|
||||
public:
|
||||
using registers = Register;
|
||||
using pointer_type = PointerType;
|
||||
using hookable_instructions = HookableInstructions;
|
||||
|
||||
static constexpr size_t pointer_size = sizeof(pointer_type);
|
||||
static constexpr registers stack_pointer = StackPointer;
|
||||
static constexpr registers instruction_pointer = InstructionPointer;
|
||||
static constexpr size_t pointer_size = sizeof(pointer_type);
|
||||
static constexpr registers stack_pointer = StackPointer;
|
||||
static constexpr registers instruction_pointer = InstructionPointer;
|
||||
|
||||
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
||||
{
|
||||
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
||||
}
|
||||
void start_from_ip(const std::chrono::nanoseconds timeout = {}, const size_t count = 0)
|
||||
{
|
||||
this->start(this->read_instruction_pointer(), 0, timeout, count);
|
||||
}
|
||||
|
||||
void write_register(registers reg, const void* value, const size_t size)
|
||||
{
|
||||
this->write_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
void write_register(registers reg, const void* value, const size_t size)
|
||||
{
|
||||
this->write_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
|
||||
void read_register(registers reg, void* value, const size_t size)
|
||||
{
|
||||
this->read_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
void read_register(registers reg, void* value, const size_t size)
|
||||
{
|
||||
this->read_raw_register(static_cast<int>(reg), value, size);
|
||||
}
|
||||
|
||||
template <typename T = pointer_type>
|
||||
T reg(const registers regid)
|
||||
{
|
||||
T value{};
|
||||
this->read_register(regid, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
template <typename T = pointer_type>
|
||||
T reg(const registers regid)
|
||||
{
|
||||
T value{};
|
||||
this->read_register(regid, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T = pointer_type, typename S>
|
||||
void reg(const registers regid, const S& maybe_value)
|
||||
{
|
||||
T value = static_cast<T>(maybe_value);
|
||||
this->write_register(regid, &value, sizeof(value));
|
||||
}
|
||||
template <typename T = pointer_type, typename S>
|
||||
void reg(const registers regid, const S& maybe_value)
|
||||
{
|
||||
T value = static_cast<T>(maybe_value);
|
||||
this->write_register(regid, &value, sizeof(value));
|
||||
}
|
||||
|
||||
pointer_type read_instruction_pointer()
|
||||
{
|
||||
return this->reg(instruction_pointer);
|
||||
}
|
||||
pointer_type read_instruction_pointer()
|
||||
{
|
||||
return this->reg(instruction_pointer);
|
||||
}
|
||||
|
||||
pointer_type read_stack_pointer()
|
||||
{
|
||||
return this->reg(stack_pointer);
|
||||
}
|
||||
pointer_type read_stack_pointer()
|
||||
{
|
||||
return this->reg(stack_pointer);
|
||||
}
|
||||
|
||||
pointer_type read_stack(const size_t index)
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
pointer_type read_stack(const size_t index)
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
|
||||
this->read_memory(sp + (index * pointer_size), &result, sizeof(result));
|
||||
this->read_memory(sp + (index * pointer_size), &result, sizeof(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void push_stack(const pointer_type& value)
|
||||
{
|
||||
const auto sp = this->read_stack_pointer() - pointer_size;
|
||||
this->reg(stack_pointer, sp);
|
||||
this->write_memory(sp, &value, sizeof(value));
|
||||
}
|
||||
void push_stack(const pointer_type& value)
|
||||
{
|
||||
const auto sp = this->read_stack_pointer() - pointer_size;
|
||||
this->reg(stack_pointer, sp);
|
||||
this->write_memory(sp, &value, sizeof(value));
|
||||
}
|
||||
|
||||
pointer_type pop_stack()
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
this->read_memory(sp, &result, sizeof(result));
|
||||
this->reg(stack_pointer, sp + pointer_size);
|
||||
pointer_type pop_stack()
|
||||
{
|
||||
pointer_type result{};
|
||||
const auto sp = this->read_stack_pointer();
|
||||
this->read_memory(sp, &result, sizeof(result));
|
||||
this->reg(stack_pointer, sp + pointer_size);
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback)
|
||||
{
|
||||
return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback));
|
||||
}
|
||||
emulator_hook* hook_instruction(hookable_instructions instruction_type, instruction_hook_callback callback)
|
||||
{
|
||||
return this->hook_instruction(static_cast<int>(instruction_type), std::move(callback));
|
||||
}
|
||||
|
||||
private:
|
||||
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
|
||||
private:
|
||||
emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) override = 0;
|
||||
|
||||
void read_raw_register(int reg, void* value, size_t size) override = 0;
|
||||
void write_raw_register(int reg, const void* value, size_t size) override = 0;
|
||||
void read_raw_register(int reg, void* value, size_t size) override = 0;
|
||||
void write_raw_register(int reg, const void* value, size_t size) override = 0;
|
||||
};
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
|
||||
enum class x64_hookable_instructions
|
||||
{
|
||||
invalid,
|
||||
syscall,
|
||||
cpuid,
|
||||
rdtsc,
|
||||
rdtscp,
|
||||
invalid,
|
||||
syscall,
|
||||
cpuid,
|
||||
rdtsc,
|
||||
rdtscp,
|
||||
};
|
||||
|
||||
using x64_emulator = typed_emulator<uint64_t, x64_register, x64_register::rip,
|
||||
x64_register::rsp, x64_hookable_instructions>;
|
||||
using x64_emulator =
|
||||
typed_emulator<uint64_t, x64_register, x64_register::rip, x64_register::rsp, x64_hookable_instructions>;
|
||||
|
||||
@@ -2,244 +2,244 @@
|
||||
|
||||
enum class x64_register
|
||||
{
|
||||
invalid = 0,
|
||||
ah,
|
||||
al,
|
||||
ax,
|
||||
bh,
|
||||
bl,
|
||||
bp,
|
||||
bpl,
|
||||
bx,
|
||||
ch,
|
||||
cl,
|
||||
cs,
|
||||
cx,
|
||||
dh,
|
||||
di,
|
||||
dil,
|
||||
dl,
|
||||
ds,
|
||||
dx,
|
||||
eax,
|
||||
ebp,
|
||||
ebx,
|
||||
ecx,
|
||||
edi,
|
||||
edx,
|
||||
eflags,
|
||||
eip,
|
||||
es = eip + 2,
|
||||
esi,
|
||||
esp,
|
||||
fpsw,
|
||||
fs,
|
||||
gs,
|
||||
ip,
|
||||
rax,
|
||||
rbp,
|
||||
rbx,
|
||||
rcx,
|
||||
rdi,
|
||||
rdx,
|
||||
rip,
|
||||
rsi = rip + 2,
|
||||
rsp,
|
||||
si,
|
||||
sil,
|
||||
sp,
|
||||
spl,
|
||||
ss,
|
||||
cr0,
|
||||
cr1,
|
||||
cr2,
|
||||
cr3,
|
||||
cr4,
|
||||
cr8 = cr4 + 4,
|
||||
dr0 = cr8 + 8,
|
||||
dr1,
|
||||
dr2,
|
||||
dr3,
|
||||
dr4,
|
||||
dr5,
|
||||
dr6,
|
||||
dr7,
|
||||
fp0 = dr7 + 9,
|
||||
fp1,
|
||||
fp2,
|
||||
fp3,
|
||||
fp4,
|
||||
fp5,
|
||||
fp6,
|
||||
fp7,
|
||||
k0,
|
||||
k1,
|
||||
k2,
|
||||
k3,
|
||||
k4,
|
||||
k5,
|
||||
k6,
|
||||
k7,
|
||||
mm0,
|
||||
mm1,
|
||||
mm2,
|
||||
mm3,
|
||||
mm4,
|
||||
mm5,
|
||||
mm6,
|
||||
mm7,
|
||||
r8,
|
||||
r9,
|
||||
r10,
|
||||
r11,
|
||||
r12,
|
||||
r13,
|
||||
r14,
|
||||
r15,
|
||||
st0,
|
||||
st1,
|
||||
st2,
|
||||
st3,
|
||||
st4,
|
||||
st5,
|
||||
st6,
|
||||
st7,
|
||||
xmm0,
|
||||
xmm1,
|
||||
xmm2,
|
||||
xmm3,
|
||||
xmm4,
|
||||
xmm5,
|
||||
xmm6,
|
||||
xmm7,
|
||||
xmm8,
|
||||
xmm9,
|
||||
xmm10,
|
||||
xmm11,
|
||||
xmm12,
|
||||
xmm13,
|
||||
xmm14,
|
||||
xmm15,
|
||||
xmm16,
|
||||
xmm17,
|
||||
xmm18,
|
||||
xmm19,
|
||||
xmm20,
|
||||
xmm21,
|
||||
xmm22,
|
||||
xmm23,
|
||||
xmm24,
|
||||
xmm25,
|
||||
xmm26,
|
||||
xmm27,
|
||||
xmm28,
|
||||
xmm29,
|
||||
xmm30,
|
||||
xmm31,
|
||||
ymm0,
|
||||
ymm1,
|
||||
ymm2,
|
||||
ymm3,
|
||||
ymm4,
|
||||
ymm5,
|
||||
ymm6,
|
||||
ymm7,
|
||||
ymm8,
|
||||
ymm9,
|
||||
ymm10,
|
||||
ymm11,
|
||||
ymm12,
|
||||
ymm13,
|
||||
ymm14,
|
||||
ymm15,
|
||||
ymm16,
|
||||
ymm17,
|
||||
ymm18,
|
||||
ymm19,
|
||||
ymm20,
|
||||
ymm21,
|
||||
ymm22,
|
||||
ymm23,
|
||||
ymm24,
|
||||
ymm25,
|
||||
ymm26,
|
||||
ymm27,
|
||||
ymm28,
|
||||
ymm29,
|
||||
ymm30,
|
||||
ymm31,
|
||||
zmm0,
|
||||
zmm1,
|
||||
zmm2,
|
||||
zmm3,
|
||||
zmm4,
|
||||
zmm5,
|
||||
zmm6,
|
||||
zmm7,
|
||||
zmm8,
|
||||
zmm9,
|
||||
zmm10,
|
||||
zmm11,
|
||||
zmm12,
|
||||
zmm13,
|
||||
zmm14,
|
||||
zmm15,
|
||||
zmm16,
|
||||
zmm17,
|
||||
zmm18,
|
||||
zmm19,
|
||||
zmm20,
|
||||
zmm21,
|
||||
zmm22,
|
||||
zmm23,
|
||||
zmm24,
|
||||
zmm25,
|
||||
zmm26,
|
||||
zmm27,
|
||||
zmm28,
|
||||
zmm29,
|
||||
zmm30,
|
||||
zmm31,
|
||||
r8b,
|
||||
r9b,
|
||||
r10b,
|
||||
r11b,
|
||||
r12b,
|
||||
r13b,
|
||||
r14b,
|
||||
r15b,
|
||||
r8d,
|
||||
r9d,
|
||||
r10d,
|
||||
r11d,
|
||||
r12d,
|
||||
r13d,
|
||||
r14d,
|
||||
r15d,
|
||||
r8w,
|
||||
r9w,
|
||||
r10w,
|
||||
r11w,
|
||||
r12w,
|
||||
r13w,
|
||||
r14w,
|
||||
r15w,
|
||||
idtr,
|
||||
gdtr,
|
||||
ldtr,
|
||||
tr,
|
||||
fpcw,
|
||||
fptag,
|
||||
msr,
|
||||
mxcsr,
|
||||
fs_base,
|
||||
gs_base,
|
||||
flags,
|
||||
rflags,
|
||||
fip,
|
||||
fcs,
|
||||
fdp,
|
||||
fds,
|
||||
fop,
|
||||
end, // Must be last
|
||||
invalid = 0,
|
||||
ah,
|
||||
al,
|
||||
ax,
|
||||
bh,
|
||||
bl,
|
||||
bp,
|
||||
bpl,
|
||||
bx,
|
||||
ch,
|
||||
cl,
|
||||
cs,
|
||||
cx,
|
||||
dh,
|
||||
di,
|
||||
dil,
|
||||
dl,
|
||||
ds,
|
||||
dx,
|
||||
eax,
|
||||
ebp,
|
||||
ebx,
|
||||
ecx,
|
||||
edi,
|
||||
edx,
|
||||
eflags,
|
||||
eip,
|
||||
es = eip + 2,
|
||||
esi,
|
||||
esp,
|
||||
fpsw,
|
||||
fs,
|
||||
gs,
|
||||
ip,
|
||||
rax,
|
||||
rbp,
|
||||
rbx,
|
||||
rcx,
|
||||
rdi,
|
||||
rdx,
|
||||
rip,
|
||||
rsi = rip + 2,
|
||||
rsp,
|
||||
si,
|
||||
sil,
|
||||
sp,
|
||||
spl,
|
||||
ss,
|
||||
cr0,
|
||||
cr1,
|
||||
cr2,
|
||||
cr3,
|
||||
cr4,
|
||||
cr8 = cr4 + 4,
|
||||
dr0 = cr8 + 8,
|
||||
dr1,
|
||||
dr2,
|
||||
dr3,
|
||||
dr4,
|
||||
dr5,
|
||||
dr6,
|
||||
dr7,
|
||||
fp0 = dr7 + 9,
|
||||
fp1,
|
||||
fp2,
|
||||
fp3,
|
||||
fp4,
|
||||
fp5,
|
||||
fp6,
|
||||
fp7,
|
||||
k0,
|
||||
k1,
|
||||
k2,
|
||||
k3,
|
||||
k4,
|
||||
k5,
|
||||
k6,
|
||||
k7,
|
||||
mm0,
|
||||
mm1,
|
||||
mm2,
|
||||
mm3,
|
||||
mm4,
|
||||
mm5,
|
||||
mm6,
|
||||
mm7,
|
||||
r8,
|
||||
r9,
|
||||
r10,
|
||||
r11,
|
||||
r12,
|
||||
r13,
|
||||
r14,
|
||||
r15,
|
||||
st0,
|
||||
st1,
|
||||
st2,
|
||||
st3,
|
||||
st4,
|
||||
st5,
|
||||
st6,
|
||||
st7,
|
||||
xmm0,
|
||||
xmm1,
|
||||
xmm2,
|
||||
xmm3,
|
||||
xmm4,
|
||||
xmm5,
|
||||
xmm6,
|
||||
xmm7,
|
||||
xmm8,
|
||||
xmm9,
|
||||
xmm10,
|
||||
xmm11,
|
||||
xmm12,
|
||||
xmm13,
|
||||
xmm14,
|
||||
xmm15,
|
||||
xmm16,
|
||||
xmm17,
|
||||
xmm18,
|
||||
xmm19,
|
||||
xmm20,
|
||||
xmm21,
|
||||
xmm22,
|
||||
xmm23,
|
||||
xmm24,
|
||||
xmm25,
|
||||
xmm26,
|
||||
xmm27,
|
||||
xmm28,
|
||||
xmm29,
|
||||
xmm30,
|
||||
xmm31,
|
||||
ymm0,
|
||||
ymm1,
|
||||
ymm2,
|
||||
ymm3,
|
||||
ymm4,
|
||||
ymm5,
|
||||
ymm6,
|
||||
ymm7,
|
||||
ymm8,
|
||||
ymm9,
|
||||
ymm10,
|
||||
ymm11,
|
||||
ymm12,
|
||||
ymm13,
|
||||
ymm14,
|
||||
ymm15,
|
||||
ymm16,
|
||||
ymm17,
|
||||
ymm18,
|
||||
ymm19,
|
||||
ymm20,
|
||||
ymm21,
|
||||
ymm22,
|
||||
ymm23,
|
||||
ymm24,
|
||||
ymm25,
|
||||
ymm26,
|
||||
ymm27,
|
||||
ymm28,
|
||||
ymm29,
|
||||
ymm30,
|
||||
ymm31,
|
||||
zmm0,
|
||||
zmm1,
|
||||
zmm2,
|
||||
zmm3,
|
||||
zmm4,
|
||||
zmm5,
|
||||
zmm6,
|
||||
zmm7,
|
||||
zmm8,
|
||||
zmm9,
|
||||
zmm10,
|
||||
zmm11,
|
||||
zmm12,
|
||||
zmm13,
|
||||
zmm14,
|
||||
zmm15,
|
||||
zmm16,
|
||||
zmm17,
|
||||
zmm18,
|
||||
zmm19,
|
||||
zmm20,
|
||||
zmm21,
|
||||
zmm22,
|
||||
zmm23,
|
||||
zmm24,
|
||||
zmm25,
|
||||
zmm26,
|
||||
zmm27,
|
||||
zmm28,
|
||||
zmm29,
|
||||
zmm30,
|
||||
zmm31,
|
||||
r8b,
|
||||
r9b,
|
||||
r10b,
|
||||
r11b,
|
||||
r12b,
|
||||
r13b,
|
||||
r14b,
|
||||
r15b,
|
||||
r8d,
|
||||
r9d,
|
||||
r10d,
|
||||
r11d,
|
||||
r12d,
|
||||
r13d,
|
||||
r14d,
|
||||
r15d,
|
||||
r8w,
|
||||
r9w,
|
||||
r10w,
|
||||
r11w,
|
||||
r12w,
|
||||
r13w,
|
||||
r14w,
|
||||
r15w,
|
||||
idtr,
|
||||
gdtr,
|
||||
ldtr,
|
||||
tr,
|
||||
fpcw,
|
||||
fptag,
|
||||
msr,
|
||||
mxcsr,
|
||||
fs_base,
|
||||
gs_base,
|
||||
flags,
|
||||
rflags,
|
||||
fip,
|
||||
fcs,
|
||||
fdp,
|
||||
fds,
|
||||
fop,
|
||||
end, // Must be last
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user