Add readable operand decode and register def-use analysis

insndec.h: insn_decode() decodes one instruction into a readable form -
concrete registers as canonical roots (al/ah/ax/eax/rax -> one root), memory
base/index/scale/disp, immediate values, operand sizes, per-operand read/write,
and implicit operands/effects (rsp on push/pop, rdx:rax on mul/div, FLAGS). It
is ABI-agnostic: a call reports only its architectural effects.

codetrace.h: func_usedef(fn, abi, ...) computes per-instruction register
use/def over a function view (reusing cfg_blocks), splitting writes into
full-kill / partial / conditional so a reaching-definitions pass can be built on
top. With TRACE_ABI_WIN64 it marks the Win64 caller-saved set
(rax,rcx,rdx,r8-r11,xmm0-5,flags) clobbered at each call site; the clobber table
is confined to codetrace.c and the generic layer stays OS-agnostic
(TRACE_ABI_NONE keeps the conservative behaviour).

Both ride the existing optional disassembler seam (a second insn_decode_full
is added to semsig_backend.h; sem_insn and semsig_hash are untouched), gated
behind VMIE_HAVE_DISASM; OFF builds a stub. Adds the vmie_win32_func_usedef
wrapper. No new build option - the feature shares VMIE_DISASM with semsig.
This commit is contained in:
2026-06-28 23:02:40 +03:00
parent 51feb8d39c
commit 244511b66e
11 changed files with 1133 additions and 1 deletions
+35
View File
@@ -23,6 +23,7 @@
#include "memmodel.h" /* vmie_mem, vregion/VR_*, task/range, gva_read/write/ptr/regions/sweep */
#include "sigscan.h" /* mem_view_t, sig_pattern_t */
#include "scan.h" /* scan_type, scan_ptr_path, generic scan surface */
#include "codetrace.h" /* insn_usedef, trace_abi (register def-use surface) */
/* Opaque introspection context. Completed in src/engine/win32/engine-win32.h;
* callers only ever hold a pointer. Created by vmie_win32_open(), populated by
@@ -492,6 +493,40 @@ int vmie_win32_func_imports(vmie_win32* v, uint64_t cr3, uint64_t module_base,
uint64_t vmie_win32_func_semsig(vmie_win32* v, uint64_t cr3,
uint64_t module_base, uint32_t func_rva);
/* Register def-use of ONE function, addressed by its func_rva (a .pdata function
* start, as from vmie_win32_functions or an export). The win32 convenience entry
* point over the generic func_usedef (codetrace.h): it locates the function
* extent from .pdata (like vmie_win32_func_semsig), gathers its body with
* gva_read, builds a SECTION_LOCAL mem_view_t, and DELEGATES to func_usedef -
* PASSING TRACE_ABI_WIN64, because choosing the Win64 calling convention is the
* OS-specific decision that belongs in this layer (which already owns .pdata /
* cr3 / module_base). It does NOT reimplement the analysis or apply the volatile
* clobber itself (func_usedef does), and pulls in no disassembler backend (the
* feature is inherited transitively via the same VMIE_HAVE_DISASM as the generic).
*
* v - engine handle.
* cr3 - the process address space the module is mapped in.
* module_base - image base VA (its PE headers must be resident).
* func_rva - function start RVA (must match a .pdata function start).
* out, max - caller array receiving up to `max` per-instruction insn_usedef
* records in ascending function-local offset order (NULL to count).
*
* Returns the TOTAL instruction count, 0 if the function is empty, or -1 on any
* of: func_rva is not a known function start, the body is unreadable (paged out),
* a decode desync, or a build with no disassembler backend (VMIE_HAVE_DISASM==0).
* The off field of each record is a function-local byte offset (SECTION_LOCAL).
*
* The Win64 volatile clobber applied at call sites - and its honest limits (only
* the standard Win64 convention; not __vectorcall/custom; the return value is not
* a separate def) - are documented at the clobber table in codetrace.c.
*
* Example - registers each instruction of a function kills:
* insn_usedef ud[512];
* int n = vmie_win32_func_usedef(v, pr->cr3, m.base, fn_rva, ud, 512);
* for (int i = 0; i < n && i < 512; i++) ... inspect ud[i].def ... */
int vmie_win32_func_usedef(vmie_win32* v, uint64_t cr3, uint64_t module_base,
uint32_t func_rva, insn_usedef* out, int max);
/* Devirtualization (C++ vtables) needs NO dedicated symbol - it is a
* COMPOSITION of primitives the engine already exposes:
* - a vtable at `vtable_va` is an array of code pointers, so its METHODS are