[Audit] --audit-r3-dump-bytes: dump N bytes at r3 when probe fires

AUDIT-059 round 15 — diagnostic. New cvar `audit_jit_prolog_r3_bytes`
(default 64 = existing behaviour, capped at 256, rounded up to 16B
multiple) controls how many bytes are dumped at host(r3) when the
audit_jit_prolog_pc probe fires. Set to 80 to capture audit-051's
stack-local struct at sub_82452DC0's r31+96.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-07 19:39:26 +02:00
parent 87e4a67b61
commit 4670afbeb5

View File

@@ -58,6 +58,13 @@ DEFINE_uint32(audit_jit_prolog_mem_dump, 0,
"and its vtable[0] (first virtual method = bctrl target at " "and its vtable[0] (first virtual method = bctrl target at "
"sub_822F1AA8+0x90). Zero (default) disables.", "sub_822F1AA8+0x90). Zero (default) disables.",
"Auditing"); "Auditing");
DEFINE_uint32(audit_jit_prolog_r3_bytes, 0x40,
"Audit-052 — number of bytes to dump from host(r3) on every "
"audit_jit_prolog_pc fire (capped at 256, 16-byte aligned). "
"Default 64 (existing behaviour). Set to 80 to capture the "
"audit-051 stack-local struct at sub_82452DC0's r31+96 "
"(probe sub_8245B000 entry where r3 IS the struct ptr).",
"Auditing");
DEFINE_bool(ignore_undefined_externs, true, DEFINE_bool(ignore_undefined_externs, true,
"Don't exit when an undefined extern is called.", "CPU"); "Don't exit when an undefined extern is called.", "CPU");
DEFINE_bool(emit_source_annotations, false, DEFINE_bool(emit_source_annotations, false,
@@ -491,11 +498,16 @@ uint64_t AuditLogJitPrologArgs(void* raw_context, uint64_t /*unused*/) {
"r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}", "r6={:08X} r7={:08X} r8={:08X} r9={:08X} r10={:08X} lr={:08X}",
pc, tid, r3, r4, r5, r6, r7, r8, r9, r10, lr); pc, tid, r3, r4, r5, r6, r7, r8, r9, r10, lr);
// Dump 64 bytes at host(r3) if r3 looks like a plausible guest VA. // Dump N bytes at host(r3) if r3 looks like a plausible guest VA.
// N comes from cvar `audit_jit_prolog_r3_bytes` (default 64 = existing
// behaviour). Round up to a 16-byte multiple; cap at 256.
uint32_t r3_dump_bytes = static_cast<uint32_t>(cvars::audit_jit_prolog_r3_bytes);
if (r3_dump_bytes > 256) r3_dump_bytes = 256;
r3_dump_bytes = (r3_dump_bytes + 15) & ~15u;
if (r3 >= 0x10000 && r3 < 0xE0000000) { if (r3 >= 0x10000 && r3 < 0xE0000000) {
uint8_t* host = ctx->TranslateVirtual(r3); uint8_t* host = ctx->TranslateVirtual(r3);
if (host) { if (host) {
for (uint32_t off = 0; off < 0x40; off += 16) { for (uint32_t off = 0; off < r3_dump_bytes; off += 16) {
uint32_t d0 = xe::load_and_swap<uint32_t>(host + off + 0); uint32_t d0 = xe::load_and_swap<uint32_t>(host + off + 0);
uint32_t d1 = xe::load_and_swap<uint32_t>(host + off + 4); uint32_t d1 = xe::load_and_swap<uint32_t>(host + off + 4);
uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8); uint32_t d2 = xe::load_and_swap<uint32_t>(host + off + 8);