Internals¶
For contributors modifying the framework itself. This is the machinery behind Architecture.
The annotation cache¶
ObfuscationAnnotationAnalysis (obf-annotations) is a module analysis. It walks
llvm.global.annotations once, parses each "obf: ..." spec, and builds a cached
Function → ObfuscationConfig map. Because it's an analysis, the result is computed once and shared
by every downstream pass — parsing never repeats.
FunctionObfContextAnalysis (function-obf-context) is the per-function view: it exposes the
resolved config plus the derived seeds to each function pass, so a pass never re-parses or re-derives.
The config type¶
ObfuscationConfig (in ObfuscationConfig.*) holds per-pass enablement and parameters. Parsing rules
(keys, quoting, booleans) and merging semantics (additive enablement, last-wins params) live here.
When you add a pass, this is where its parameters get parsed and stored — one of the seven
wiring touch-points.
The driver & ordering¶
ObfuscationModulePass (obfuscation) is the entry pass. It runs module-only passes (fmerge, then
strenc), then invokes the function driver ObfuscationFunctionDriverPass (obfuscation-fn).
The driver resolves the pipeline order in ObfuscationPipeline.* — a topological sort with
conflict enforcement (e.g. vm + flattening is rejected). Two CLI overrides exist
(-obf-pipeline-ordering, -obf-pipeline-ordering-ann), both of which still run the conflict checks.
When you add a pass, its position in the topological order is declared here.
Seed derivation¶
Seeds cascade base → module → function → pass, each level a stable hash of the level above plus a
stable key (module id, function name, canonical pass id). Passes must draw randomness from the
provided RNG / deriveSeed — never from random_device directly, never from hash-map iteration
order — or determinism breaks. See Determinism & seeds.
Registration¶
registration/ObfPasses.inc is the single source of truth for registered pass/analysis names,
consumed by registration/PluginEntry.cpp for both build modes. It registers analyses too, not
just passes — the driver queries FunctionObfContextAnalysis and the module pass queries
ObfReportAnalysis, so a registration that omits the analyses asserts at runtime.
Reporting¶
ObfReportAnalysis (obf-report) collects the JSON obfuscation map and per-pass CFG snapshots. A
pass reports its status/deltas/skip-reason through this, which is what surfaces in the HTML viewer and
the report JSON. Emitting a skip reason (rather than silently doing nothing) is expected when a pass
declines a function.
Gotcha: erasing annotated functions¶
fmerge erases merged originals. Any pass or analysis that reads llvm.global.annotations must
account for a function's annotation use being consumed and the function possibly no longer existing
after merge — handle the use/erase ordering carefully or you'll dereference a dead Function*.
Deeper reference¶
The repository's docs/DEV.md and docs/VM.md carry the exhaustive internals (VM engine builders,
ISA extension points, the ADec registry). This page is the map; those are the territory.