Support accurate instruction counts

This commit is contained in:
Maurice Heumann
2025-04-04 13:13:09 +02:00
parent 204159f137
commit 24df7c65c2
13 changed files with 27 additions and 60 deletions

View File

@@ -9,7 +9,7 @@ struct cpu_interface
{
virtual ~cpu_interface() = default;
virtual void start(uint64_t start, uint64_t end = 0, std::chrono::nanoseconds timeout = {}, size_t count = 0) = 0;
virtual void start(size_t count = 0) = 0;
virtual void stop() = 0;
virtual size_t read_raw_register(int reg, void* value, size_t size) = 0;

View File

@@ -15,11 +15,6 @@ class typed_emulator : public emulator
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);
}
size_t write_register(registers reg, const void* value, const size_t size)
{
return this->write_raw_register(static_cast<int>(reg), value, size);

View File

@@ -31,7 +31,7 @@ extern "C"
void icicle_remove_syscall_hook(icicle_emulator*, uint32_t id);
size_t icicle_read_register(icicle_emulator*, int reg, void* data, size_t length);
size_t icicle_write_register(icicle_emulator*, int reg, const void* data, size_t length);
void icicle_start(icicle_emulator*);
void icicle_start(icicle_emulator*, size_t count);
void icicle_stop(icicle_emulator*);
void icicle_destroy_emulator(icicle_emulator*);
}
@@ -115,18 +115,9 @@ namespace icicle
}
}
void start(const uint64_t start, const uint64_t end, std::chrono::nanoseconds timeout,
const size_t count) override
void start(const size_t count) override
{
if (timeout.count() < 0)
{
timeout = {};
}
(void)start;
(void)end;
(void)count;
icicle_start(this->emu_);
icicle_start(this->emu_, count);
}
void stop() override

View File

@@ -197,8 +197,11 @@ impl IcicleEmulator {
return &mut self.vm.cpu.mem;
}
pub fn start(&mut self) {
self.vm.icount_limit = u64::MAX;
pub fn start(&mut self, count: u64) {
self.vm.icount_limit = match count {
0 => u64::MAX,
_ => self.vm.cpu.icount + count,
};
loop {
let reason = self.vm.run();
@@ -255,7 +258,7 @@ impl IcicleEmulator {
}
pub fn stop(&mut self) {
self.vm.icount_limit = self.vm.cpu.icount;
self.vm.icount_limit = 0;
}
pub fn add_violation_hook(&mut self, callback: Box<dyn Fn(u64, u8, bool) -> bool>) -> u32 {

View File

@@ -20,10 +20,10 @@ pub fn icicle_create_emulator() -> *mut c_void {
}
#[unsafe(no_mangle)]
pub fn icicle_start(ptr: *mut c_void) {
pub fn icicle_start(ptr: *mut c_void, count: usize) {
unsafe {
let emulator = &mut *(ptr as *mut IcicleEmulator);
emulator.start();
emulator.start(count as u64);
}
}

View File

@@ -269,17 +269,12 @@ namespace unicorn
uc_close(this->uc_);
}
void start(const uint64_t start, const uint64_t end, std::chrono::nanoseconds timeout,
const size_t count) override
void start(const size_t count) override
{
if (timeout.count() < 0)
{
timeout = {};
}
this->has_violation_ = false;
const auto timeoutYs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
const auto res = uc_emu_start(*this, start, end, static_cast<uint64_t>(timeoutYs.count()), count);
const auto start = this->read_instruction_pointer();
constexpr auto end = std::numeric_limits<uint64_t>::max();
const auto res = uc_emu_start(*this, start, end, 0, count);
if (res == UC_ERR_OK)
{
return;

View File

@@ -15,7 +15,7 @@ namespace test
constexpr auto count = 200000;
auto emu = create_sample_emulator();
emu.start({}, count);
emu.start(count);
ASSERT_EQ(emu.get_executed_instructions(), count);
}
@@ -34,12 +34,12 @@ namespace test
constexpr auto offset = 1;
const auto instructionsToExecute = executedInstructions - offset;
new_emu.start({}, instructionsToExecute);
new_emu.start(instructionsToExecute);
ASSERT_EQ(new_emu.get_executed_instructions(), instructionsToExecute);
ASSERT_NOT_TERMINATED(new_emu);
new_emu.start({}, offset);
new_emu.start(offset);
ASSERT_TERMINATED_SUCCESSFULLY(new_emu);
ASSERT_EQ(new_emu.get_executed_instructions(), executedInstructions);

View File

@@ -141,7 +141,7 @@ namespace test
const auto get_state_for_count = [&](const size_t count) {
reset_emulator();
emu.start({}, count);
emu.start(count);
utils::buffer_serializer state{};
emu.serialize(state);

View File

@@ -77,7 +77,7 @@ namespace test
TEST(SerializationTest, DeserializedEmulatorBehavesLikeSource)
{
auto emu = create_sample_emulator();
emu.start({}, 100);
emu.start(100);
utils::buffer_serializer serializer{};
emu.serialize(serializer);

View File

@@ -514,15 +514,10 @@ void windows_emulator::setup_hooks()
[&](const uint64_t address, const size_t, const uint64_t) { this->on_instruction_execution(address); });
}
void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
void windows_emulator::start(size_t count)
{
const auto use_count = count > 0;
const auto use_timeout = timeout != std::chrono::nanoseconds{};
const auto start_time = std::chrono::high_resolution_clock::now();
const auto start_instructions = this->executed_instructions_;
const auto target_time = start_time + timeout;
const auto target_instructions = start_instructions + count;
while (true)
@@ -532,25 +527,13 @@ void windows_emulator::start(std::chrono::nanoseconds timeout, size_t count)
this->perform_thread_switch();
}
this->emu().start_from_ip(timeout, count);
this->emu().start(count);
if (!this->switch_thread_ && !this->emu().has_violation())
{
break;
}
if (use_timeout)
{
const auto now = std::chrono::high_resolution_clock::now();
if (now >= target_time)
{
break;
}
timeout = target_time - now;
}
if (use_count)
{
const auto current_instructions = this->executed_instructions_;

View File

@@ -130,7 +130,7 @@ class windows_emulator
return this->executed_instructions_;
}
void start(std::chrono::nanoseconds timeout = {}, size_t count = 0);
void start(size_t count = 0);
void serialize(utils::buffer_serializer& buffer) const;
void deserialize(utils::buffer_deserializer& buffer);

View File

@@ -37,7 +37,7 @@ class win_x64_gdb_stub_handler : public x64_gdb_stub_handler
{
try
{
this->win_emu_->start({}, 1);
this->win_emu_->start(1);
}
catch (const std::exception& e)
{

View File

@@ -64,7 +64,7 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
{
try
{
this->emu_->start_from_ip();
this->emu_->start();
}
catch (const std::exception& e)
{
@@ -78,7 +78,7 @@ class x64_gdb_stub_handler : public gdb_stub::debugging_handler
{
try
{
this->emu_->start_from_ip({}, 1);
this->emu_->start(1);
}
catch (const std::exception& e)
{