// internal.cpp — status_str + shared file/error helpers. #include "core/internal.hpp" #include "core/target.hpp" #include #include #include #include #include #include #include namespace mf { const char *status_str(Status s) noexcept { switch (s) { case Status::ok: return "ok"; case Status::argument: return "argument"; case Status::io: return "io"; case Status::emulator: return "emulator"; case Status::boot: return "boot"; case Status::budget: return "budget"; case Status::fault: return "fault"; case Status::unsupported: return "unsupported"; case Status::state: return "state"; case Status::resource: return "resource"; } return "?"; } namespace detail { const char *uc_err_str(uc_err e) { const char *s = uc_strerror(e); return s != nullptr ? s : "unknown"; } bool unmapped_hook(HookCtx &ctx, uc_mem_type /*type*/, uint64_t address, int /*size*/, int64_t /*value*/) { if (ctx.policy == UnmapPolicy::fail) { return false; // not handled → Unicorn aborts the run } // uc_mem_map alone is enough: newly mapped memory is guaranteed // zero-filled — qemu_ram_alloc hosts come from qemu_anon_ram_alloc, // i.e. an anonymous mmap (POSIX) / VirtualAlloc MEM_COMMIT (Windows). // Verified against the vendored sources: uc_mem_map → memory_map → // memory_region_init_ram → qemu_ram_alloc (unicorn/qemu/exec.c, // util/oslib-posix.c). const uint64_t base = address & ~0xFFFFULL; const uc_err e = uc_mem_map(&ctx.uc(), base, 0x10000, UC_PROT_ALL); if (e != UC_ERR_OK) { // UC_ERR_MAP: the 64 KiB region overlaps an existing mapping // (fault near a mapped boundary, e.g. the flash hole above the // payload). uc_mem_map maps nothing in that case — returning // true would retry the access and spin forever, so report the // fault instead. return false; } if (ctx.counters != nullptr) { ctx.counters->unmapped_faults.fetch_add(1, std::memory_order_relaxed); } return true; // handled: continue after the access } std::vector read_file(const std::string &path, size_t max_size) { FILE *f = std::fopen(path.c_str(), "rb"); if (f == nullptr) { throw Error{Status::io, "cannot open file"}; } std::fseek(f, 0, SEEK_END); long const file_size = std::ftell(f); std::fseek(f, 0, SEEK_SET); if (file_size < 0) { std::fclose(f); throw Error{Status::io, "bad file size"}; } auto sz = static_cast(file_size); if (max_size > 0 && sz > max_size) { sz = max_size; } std::vector buf(sz); if (sz > 0 && std::fread(buf.data(), 1, sz, f) != sz) { std::fclose(f); throw Error{Status::io, "short read"}; } std::fclose(f); return buf; } void write_file(const std::string &path, std::span bytes) { FILE *f = std::fopen(path.c_str(), "wb"); if (f == nullptr) { throw Error{Status::io, "cannot create file"}; } if (!bytes.empty() && std::fwrite(bytes.data(), 1, bytes.size(), f) != bytes.size()) { std::fclose(f); throw Error{Status::io, "short write"}; } std::fclose(f); } } // namespace detail uc_err Target::emu_start(uint64_t begin, uint64_t until) { return ::uc_emu_start(uc_, begin, until, 0, 0); } } // namespace mf