1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Parse and inspect Nim-compiled native binaries.
//!
//! This crate provides a pure-Rust parser and forensic-artifact extractor for
//! binaries produced by the Nim compiler via its C, C++, or Objective-C
//! backends. A Nim-compiled program is a normal ELF, PE, or Mach-O executable
//! — there is no Nim-specific container. The crate recovers Nim **runtime
//! artifacts** left inside an otherwise-ordinary native binary.
//!
//! # What it extracts
//!
//! | Artifact | Method | Notes |
//! |----------|--------|-------|
//! | Detection verdict | [`NimBinary::is_nim`] | 11 independent probes |
//! | Format / arch / GC mode | [`NimBinary::format`], [`NimBinary::gc_mode`] | ELF, PE, Mach-O; refc vs ARC/ORC |
//! | Entry shims | [`NimBinary::entry_shims`] | `NimMain`, `PreMain`, etc. |
//! | Init functions | [`NimBinary::init_functions`] | With decoded module paths |
//! | Module map | [`NimBinary::module_map`] | Per-module: symbols, sizes, init VAs, leaked paths |
//! | RTTI globals | [`NimBinary::rtti_symbols`] | V1 (`TNimType`) and V2 (`TNimTypeV2`) with parsed fields |
//! | Type graph | [`NimBinary::types`] | Cross-linked types: members, offsets, sizes, layout, inheritance, enum values, destructors |
//! | Code entrypoints | [`NimBinary::code_entrypoints`] | One VA-tagged stream of shims, inits, procs, raise-enclosing fns, RTTI procs |
//! | String literals | [`NimBinary::string_literals_v2`] | V2 `NIM_STRLIT_FLAG` scan |
//! | Stack-trace metadata | [`NimBinary::stack_trace`] | Proc names + `.nim` file paths (build-host leaks) |
//! | Nimble path leaks | [`NimBinary::nimble_paths`] | Package name, version, hash, username, OS |
//! | Exception types | [`NimBinary::exception_types`] | `*Error`, `*Defect` cstrings in rodata |
//! | Raise sites | [`NimBinary::raise_sites`] | Full (type, proc, file, line) tuples via instruction analysis |
//! | Demangled symbols | [`demangle::symbol::parse`] | Identifier, module, item ID |
//!
//! # Quick start
//!
//! ```no_run
//! use nimrod::NimBinary;
//!
//! let data = std::fs::read("sample").unwrap();
//! let bin = NimBinary::from_bytes(&data).unwrap();
//!
//! if !bin.is_nim() {
//! eprintln!("not a Nim binary");
//! return;
//! }
//!
//! // Detection and classification
//! println!("format: {:?}, gc: {:?}", bin.format(), bin.gc_mode());
//!
//! // Module map: which Nim modules are compiled in, with every function
//! let mmap = bin.module_map();
//! for (name, info) in &mmap.modules {
//! println!("{name}: {} functions", info.symbol_count());
//! for sym in &info.symbols {
//! // sym.name = demangled Nim identifier
//! // sym.address = VA (start disassembling here)
//! // sym.size = byte count (ELF; 0 on Mach-O/PE)
//! let _ = (sym.name.as_str(), sym.address, sym.size);
//! }
//! }
//!
//! // Raise sites: exception type + enclosing function + source location
//! for rs in bin.raise_sites() {
//! let _ = (
//! rs.exception_type.as_deref(), // "ValueError"
//! rs.enclosing_function.as_deref(), // "parseHexInt__strutils_u1234"
//! rs.file.as_deref(), // "strutils.nim"
//! rs.line, // Some(1242)
//! );
//! }
//! ```
//!
//! # Design
//!
//! - **Pure Rust**, `#![deny(unsafe_code)]`.
//! - **Cross-format**: ELF, PE, Mach-O via [`goblin`](https://docs.rs/goblin).
//! - **Zero-copy** where possible — borrows from the input byte slice.
//! - **Forensic-oriented**: prioritises attribution-grade artifacts (build-host
//! paths, package refs, exception locations) over pretty-printing.
//!
//! The format-level research backing every probe and struct layout is
//! documented in `RESEARCH.md` at the crate root.
//!
//! # Robustness contract
//!
//! nimrod parses adversarial input, so every public artifact iterator obeys a
//! uniform contract:
//!
//! - **Never panics** on malformed input. The crate denies `unwrap`, `expect`,
//! `panic`, unchecked indexing, and unchecked arithmetic in library code
//! (see the `[lints]` table in `Cargo.toml`).
//! - **Returns empty on missing data.** A walker whose artifact is absent (no
//! V2 strings in a refc build, no raise sites in a binary that never raises)
//! returns an empty slice, not an error.
//! - **Skips malformed records individually.** A single undecodable record
//! (e.g. an RTTI global whose struct bytes are not file-backed) is dropped or
//! degraded to a name-only entry; the rest of the scan still completes.
//! Name-only RTTI entries are flagged by [`NimType::is_readable`].
//!
//! # Address space
//!
//! Every address-bearing field returned by this crate is a **virtual address**
//! in the input image's load space, never a file offset. Convert to a
//! disassembler-relative RVA with the `*_rva` helpers on [`NimBinary`], with
//! [`Container::va_to_rva`], or with [`va_to_i64`] for signed-integer storage.
//!
//! # Thread safety
//!
//! [`NimBinary`] is `Send + Sync` (enforced by a compile-time assertion), so it
//! can be shared across threads or held across `.await` points. Its scan caches
//! use [`std::sync::OnceLock`].
// `missing_docs`, `unsafe_code`, plus the clippy panic-prevention set
// (`unwrap_used`, `expect_used`, `panic`, `arithmetic_side_effects`,
// `indexing_slicing`) are declared in `Cargo.toml` under `[lints]` so
// they enforce on every build regardless of the consuming workspace.
// nimrod is used in malware-analysis pipelines where every input byte
// is adversarial and the parser must not panic.
pub use va_to_i64;
pub use NimBinary;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ExceptionRef;
pub use ;
pub use ;
pub use ;
pub use RaiseSite;
pub use ;
pub use StringLiteralV1;
pub use StringLiteral;
pub use ;