ud_arch_codec/registry.rs
1//! Open registry of [`ArchCodec`] factories.
2//!
3//! Arch crates register a factory function at process startup
4//! (typically from `<crate>::register()`); resolution happens at
5//! the entry of every compile / decompile pipeline call that needs
6//! to encode arch-specific instructions.
7//!
8//! The registry takes raw `(arch_name, e_machine)` pairs rather
9//! than an AST type — that's how this crate stays independent of
10//! `ud-ast` (which would otherwise cause a cycle through
11//! `ud-arch-x86`). Callers (`ud-translate`) extract the pair from
12//! a parsed `ud_ast::Module` and feed it in.
13
14use std::sync::{OnceLock, RwLock};
15
16use crate::{ArchCodec, ArchError};
17
18/// A codec factory: inspects an `(arch_name, e_machine)` pair and
19/// returns a codec instance if it recognises the arch, else
20/// `None`. `arch_name` is the lowercase friendly arch identifier
21/// from the parsed `@module` block (`"x86_64"`, `"bpf"`, etc.);
22/// `e_machine` is the numeric ELF machine type (`EM_BPF = 247`,
23/// `EM_SBF = 263`, …) — useful for sub-arch dispatch (e.g.
24/// distinguishing Linux eBPF from Solana SBF when both carry
25/// `arch = "bpf"`).
26pub type CodecFactory =
27 fn(arch_name: Option<&str>, e_machine: Option<u64>) -> Option<Box<dyn ArchCodec>>;
28
29static REGISTRY: OnceLock<RwLock<Vec<CodecFactory>>> = OnceLock::new();
30
31fn registry() -> &'static RwLock<Vec<CodecFactory>> {
32 REGISTRY.get_or_init(|| RwLock::new(Vec::new()))
33}
34
35/// Register a codec factory. Each arch crate exposes a
36/// `register()` pub fn that calls this once.
37///
38/// Re-registering the same factory is harmless but wasteful; the
39/// registry doesn't dedupe. Production binaries call
40/// `<arch_crate>::register()` once at startup.
41pub fn register(factory: CodecFactory) {
42 registry()
43 .write()
44 .expect("arch-codec registry poisoned")
45 .push(factory);
46}
47
48/// Resolve the codec for an `(arch_name, e_machine)` pair.
49/// Returns the first matching factory's result, or
50/// [`ArchError::UnknownArch`] if nothing claims it.
51///
52/// Pass `None` for either component when it's unknown — factories
53/// must tolerate that and either match on the other or decline.
54pub fn for_arch(
55 arch_name: Option<&str>,
56 e_machine: Option<u64>,
57) -> Result<Box<dyn ArchCodec>, ArchError> {
58 let reg = registry().read().expect("arch-codec registry poisoned");
59 for factory in reg.iter() {
60 if let Some(codec) = factory(arch_name, e_machine) {
61 return Ok(codec);
62 }
63 }
64 Err(ArchError::UnknownArch {
65 arch: arch_name.map(str::to_string),
66 e_machine,
67 })
68}
69
70/// Number of factories currently registered. Cheap; intended for
71/// tests and diagnostics.
72#[must_use]
73pub fn factory_count() -> usize {
74 registry()
75 .read()
76 .expect("arch-codec registry poisoned")
77 .len()
78}