[Phase A] Expansive extraction tracing: call args, file.read, name-hash probe
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m36s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Some checks failed
Orchestrator / Commit Message Validation (push) Has been skipped
Orchestrator / Lint (push) Failing after 1m36s
Orchestrator / Windows (x86-64) (push) Has been skipped
Orchestrator / Linux (x86-64) (push) Has been skipped
Orchestrator / Create Release (push) Has been skipped
Extend the cvar-gated JSONL event_log tracer to extract Project Sylpheed
data/behaviour (reimplementation pivot). All additive, default-off, no
behaviour change (golden rule):
- Tier 1 (--phase_a_trace_args): fill the previously-empty kernel.call
args (raw r3..r10 from PPCContext) + args_resolved.path, routed through
the existing export bridge (shim trampoline body-only).
- Tier 2 (file.read): resolve NtReadFile handle->path via the object
table (LookupObject<XFile>), emit {handle,path,offset,length,buffer_va}
so runtime reads correlate to IPFB TOC entries.
- Tier 3 (--phase_a_hash_probe=<pc,...>): mirror audit_61 — a hashprobe
HIR trap band (base 300, max 32 PCs) in ppc_hir_builder.cc + native
handler TrapPhaseAHashProbe in x64_emitter.cc that derefs r3 as a
bounded guest C-string and XELOGIs PHASE-A-HASHPROBE (log-only; cpu
backend must not reach kernel event_log). Recovers the custom IPFB
name-hash from observed (string -> hash) pairs.
New files event_log.{cc,h}; cpu_flags + ppc_hir_builder + x64_emitter
are whole-file snapshots (also carry the earlier uncommitted audit_*
probe plumbing they share). Cross-build and other instrumentation remain
uncommitted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,141 @@ DEFINE_bool(
|
||||
"unimplemented PowerPC instruction is encountered.",
|
||||
"CPU");
|
||||
|
||||
// AUDIT-061 — multi-PC branch probe. Parses cvars::audit_61_branch_probe_pcs
|
||||
// once and exposes a (pc -> trap_id) lookup table. trap_id range [200, 65535].
|
||||
// PCs outside the table are not probed. Native side reads g_audit61_pcs[idx].
|
||||
#include <vector>
|
||||
#include <string>
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace audit61 {
|
||||
constexpr uint16_t kTrapBase = 200;
|
||||
constexpr size_t kMaxPcs = 32;
|
||||
static std::vector<uint32_t> g_pcs;
|
||||
static bool g_parsed = false;
|
||||
|
||||
const std::vector<uint32_t>& pcs() {
|
||||
if (!g_parsed) {
|
||||
g_parsed = true;
|
||||
const std::string& csv = cvars::audit_61_branch_probe_pcs;
|
||||
size_t pos = 0;
|
||||
while (pos < csv.size() && g_pcs.size() < kMaxPcs) {
|
||||
size_t end = csv.find(',', pos);
|
||||
std::string tok = csv.substr(pos, end - pos);
|
||||
// strip whitespace
|
||||
while (!tok.empty() && (tok.front() == ' ' || tok.front() == '\t'))
|
||||
tok.erase(tok.begin());
|
||||
while (!tok.empty() && (tok.back() == ' ' || tok.back() == '\t'))
|
||||
tok.pop_back();
|
||||
if (!tok.empty()) {
|
||||
try {
|
||||
uint32_t v = static_cast<uint32_t>(std::stoul(tok, nullptr, 0));
|
||||
g_pcs.push_back(v);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
if (end == std::string::npos) break;
|
||||
pos = end + 1;
|
||||
}
|
||||
}
|
||||
return g_pcs;
|
||||
}
|
||||
|
||||
// Returns trap id for pc, or 0 if pc not in probe set.
|
||||
uint16_t trap_id_for(uint32_t pc) {
|
||||
const auto& v = pcs();
|
||||
for (size_t i = 0; i < v.size(); ++i) {
|
||||
if (v[i] == pc) return static_cast<uint16_t>(kTrapBase + i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace audit61
|
||||
|
||||
// AUDIT-067 — value-watch. Parses cvars::audit_67_value_watch once, exposes
|
||||
// values via vals(). Trap codes for matches start at kTrapBase = 250.
|
||||
namespace audit67 {
|
||||
constexpr uint16_t kTrapBase = 250;
|
||||
constexpr size_t kMaxVals = 4;
|
||||
static std::vector<uint32_t> g_vals;
|
||||
static bool g_parsed = false;
|
||||
|
||||
const std::vector<uint32_t>& vals() {
|
||||
if (!g_parsed) {
|
||||
g_parsed = true;
|
||||
const std::string& csv = cvars::audit_67_value_watch;
|
||||
size_t pos = 0;
|
||||
while (pos < csv.size() && g_vals.size() < kMaxVals) {
|
||||
size_t end = csv.find(',', pos);
|
||||
std::string tok = csv.substr(pos, end - pos);
|
||||
while (!tok.empty() && (tok.front() == ' ' || tok.front() == '\t'))
|
||||
tok.erase(tok.begin());
|
||||
while (!tok.empty() && (tok.back() == ' ' || tok.back() == '\t'))
|
||||
tok.pop_back();
|
||||
if (!tok.empty()) {
|
||||
try {
|
||||
uint32_t v = static_cast<uint32_t>(std::stoul(tok, nullptr, 0));
|
||||
g_vals.push_back(v);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
if (end == std::string::npos) break;
|
||||
pos = end + 1;
|
||||
}
|
||||
XELOGI("AUDIT-067-INIT csv=\"{}\" parsed_count={}", csv, g_vals.size());
|
||||
for (size_t i = 0; i < g_vals.size(); ++i) {
|
||||
XELOGI("AUDIT-067-INIT vals[{}] = 0x{:08X}", i, g_vals[i]);
|
||||
}
|
||||
}
|
||||
return g_vals;
|
||||
}
|
||||
} // namespace audit67
|
||||
|
||||
// PHASE-A hash probe — multi-PC guest probe for recovering the IPFB/IDXD
|
||||
// name-hash. Parses cvars::phase_a_hash_probe once; trap ids start at 300.
|
||||
// At each fire the native handler derefs r3 as a guest C-string (the archive
|
||||
// path being hashed) and logs it alongside r3..r6. Mirrors audit61.
|
||||
namespace hashprobe {
|
||||
constexpr uint16_t kTrapBase = 300;
|
||||
constexpr size_t kMaxPcs = 32;
|
||||
static std::vector<uint32_t> g_pcs;
|
||||
static bool g_parsed = false;
|
||||
|
||||
const std::vector<uint32_t>& pcs() {
|
||||
if (!g_parsed) {
|
||||
g_parsed = true;
|
||||
const std::string& csv = cvars::phase_a_hash_probe;
|
||||
size_t pos = 0;
|
||||
while (pos < csv.size() && g_pcs.size() < kMaxPcs) {
|
||||
size_t end = csv.find(',', pos);
|
||||
std::string tok = csv.substr(pos, end - pos);
|
||||
while (!tok.empty() && (tok.front() == ' ' || tok.front() == '\t'))
|
||||
tok.erase(tok.begin());
|
||||
while (!tok.empty() && (tok.back() == ' ' || tok.back() == '\t'))
|
||||
tok.pop_back();
|
||||
if (!tok.empty()) {
|
||||
try {
|
||||
g_pcs.push_back(static_cast<uint32_t>(std::stoul(tok, nullptr, 0)));
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
if (end == std::string::npos) break;
|
||||
pos = end + 1;
|
||||
}
|
||||
}
|
||||
return g_pcs;
|
||||
}
|
||||
|
||||
uint16_t trap_id_for(uint32_t pc) {
|
||||
const auto& v = pcs();
|
||||
for (size_t i = 0; i < v.size(); ++i) {
|
||||
if (v[i] == pc) return static_cast<uint16_t>(kTrapBase + i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace hashprobe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
@@ -174,6 +309,32 @@ bool PPCHIRBuilder::Emit(GuestFunction* function, uint32_t flags) {
|
||||
|
||||
MaybeBreakOnInstruction(address);
|
||||
|
||||
// AUDIT-061: emit a trap before this instruction if it's on the probe
|
||||
// list. The trap fires BEFORE the cmp/branch HIR emit so the native
|
||||
// handler observes cr0/cr6 set by the *previous* instruction (the cmp
|
||||
// that controls this conditional branch). ContextBarrier flushes
|
||||
// HIR temporaries to PPCContext so the handler reads consistent state.
|
||||
if (!::xe::cpu::audit61::pcs().empty()) {
|
||||
uint16_t tid = ::xe::cpu::audit61::trap_id_for(address);
|
||||
if (tid != 0) {
|
||||
Comment("--audit_61_branch_probe target");
|
||||
ContextBarrier();
|
||||
Trap(tid);
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE-A hash probe: trap before this instruction so the native handler
|
||||
// observes r3 (arg string ptr) as set by the caller. ContextBarrier
|
||||
// flushes HIR temporaries so the handler reads consistent GPRs.
|
||||
if (!::xe::cpu::hashprobe::pcs().empty()) {
|
||||
uint16_t tid = ::xe::cpu::hashprobe::trap_id_for(address);
|
||||
if (tid != 0) {
|
||||
Comment("--phase_a_hash_probe target");
|
||||
ContextBarrier();
|
||||
Trap(tid);
|
||||
}
|
||||
}
|
||||
|
||||
InstrData i;
|
||||
i.address = address;
|
||||
i.code = code;
|
||||
|
||||
Reference in New Issue
Block a user