imping_ir/lib.rs
1//! Intermediate representation (IR) types and JSON Schema for the imping
2//! toolkit.
3//!
4//! One IR file describes one build of a target. Addresses are relative to each
5//! module's image base and written as `0x` hex strings; types are referenced
6//! by [`TypeId`] into the module's type table.
7//!
8//! - [`Ir`]: the whole file, read with [`Ir::from_json_str`], written with
9//! [`Ir::to_json_pretty`] and checked with [`Ir::validate`].
10//! - [`FormatVersion`]: readers reject other major versions.
11//! - [`Module`]: one image's identity, [`TypeDef`]s, [`Function`]s,
12//! [`Global`]s and [`Vtable`]s.
13//! - `json_schema()`: the file's JSON Schema, with the `schema` feature.
14//!
15//! # Examples
16//!
17//! ```
18//! use imping_ir::{Architecture, Ir};
19//!
20//! let json = r#"{
21//! "format_version": { "major": 1, "minor": 2 },
22//! "modules": [ { "name": "target.exe", "architecture": "x64",
23//! "image_base": "0x140000000", "timestamp": "0x64b8f2a1",
24//! "image_size": "0xa2b000" } ]
25//! }"#;
26//!
27//! let ir = Ir::from_json_str(json)?;
28//! assert_eq!(ir.modules[0].architecture, Architecture::X64);
29//! assert_eq!(ir.modules[0].image_base, 0x1_4000_0000);
30//!
31//! let written = ir.to_json_pretty()?;
32//! assert_eq!(Ir::from_json_str(&written)?, ir);
33//! # Ok::<(), imping_ir::Error>(())
34//! ```
35
36#![warn(missing_docs)]
37
38mod error;
39mod function;
40mod hex;
41mod items;
42mod module;
43mod types;
44mod validate;
45mod version;
46
47pub use crate::error::{Error, Result};
48pub use crate::function::{CallingConvention, Function, Parameter, Pattern, Storage};
49pub use crate::items::{Global, Rtti, Vtable, VtableEntry};
50pub use crate::module::{Architecture, HashAlgorithm, Module, ModuleHash};
51pub use crate::types::{BitField, EnumVariant, Field, Primitive, TypeDef, TypeId, TypeKind};
52pub use crate::validate::{ValidationError, ValidationErrors};
53pub use crate::version::{CURRENT_MINOR, FormatVersion, SUPPORTED_MAJOR};
54
55use serde::{Deserialize, Serialize};
56
57/// A complete IR file: a format version and the modules of one target build.
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
60pub struct Ir {
61 /// The IR format version.
62 pub format_version: FormatVersion,
63 /// The modules of the build.
64 #[serde(default)]
65 pub modules: Vec<Module>,
66}
67
68impl Ir {
69 /// Parses IR from JSON bytes.
70 ///
71 /// The version is read first, so a file of another major version fails
72 /// with [`Error::UnsupportedVersion`] instead of a structural error.
73 ///
74 /// # Errors
75 ///
76 /// - [`Error::UnsupportedVersion`] if the major version isn't [`SUPPORTED_MAJOR`].
77 /// - [`Error::Json`] if the bytes aren't valid IR JSON.
78 pub fn from_json_slice(bytes: &[u8]) -> Result<Self> {
79 /// Just the version; the rest of the document is ignored.
80 #[derive(Deserialize)]
81 struct Header {
82 format_version: FormatVersion,
83 }
84
85 let header: Header = serde_json::from_slice(bytes)?;
86 if !header.format_version.is_supported() {
87 return Err(Error::UnsupportedVersion {
88 found: header.format_version,
89 supported_major: SUPPORTED_MAJOR,
90 });
91 }
92 Ok(serde_json::from_slice(bytes)?)
93 }
94
95 /// Parses IR from a JSON string, like [`from_json_slice`](Self::from_json_slice).
96 ///
97 /// # Errors
98 ///
99 /// Same as [`from_json_slice`](Self::from_json_slice).
100 pub fn from_json_str(text: &str) -> Result<Self> {
101 Self::from_json_slice(text.as_bytes())
102 }
103
104 /// Serializes the IR to compact JSON.
105 ///
106 /// # Errors
107 ///
108 /// [`Error::Json`] if serialization fails.
109 pub fn to_json(&self) -> Result<String> {
110 Ok(serde_json::to_string(self)?)
111 }
112
113 /// Serializes the IR to pretty-printed JSON.
114 ///
115 /// # Errors
116 ///
117 /// [`Error::Json`] if serialization fails.
118 pub fn to_json_pretty(&self) -> Result<String> {
119 Ok(serde_json::to_string_pretty(self)?)
120 }
121}
122
123/// Returns the JSON Schema of the IR file format.
124///
125/// Requires the `schema` feature. Serialize the result with `serde_json` to get
126/// the schema document.
127///
128/// # Examples
129///
130/// ```
131/// let schema = imping_ir::json_schema();
132/// let text = serde_json::to_string_pretty(&schema)?;
133/// assert!(text.contains("format_version"));
134/// # Ok::<(), serde_json::Error>(())
135/// ```
136#[cfg(feature = "schema")]
137pub fn json_schema() -> schemars::Schema {
138 schemars::schema_for!(Ir)
139}