Skip to main content

qcode/value/
varnode.rs

1//! Varnodes: named, typed memory locations used as IR operands.
2//!
3//! A [`Varnode`] represents a specific location in a [`Space`] — it has an
4//! address, a size in bytes, and belongs to exactly one space.
5//! Varnodes are used to model processor registers, global variables, and
6//! other named memory locations.
7//! Unlike [`Instruction`](crate::value::Instruction) results, they are not
8//! directly SSA form: multiple instructions can read or write the same
9//! varnode's *memory location*.
10//! The Varnode's value however is a constant.
11//!
12//! [`Space`]: crate::space::Space
13
14use std::borrow::Cow;
15
16use jstd::Identifier;
17
18use crate::{
19    context::{Context, Shared},
20    error::Result,
21    space::{Space, SpaceId, SpaceRef},
22    value::{
23        Value, ValueId,
24        util::{
25            base_ref::{BaseRef, WithCtx, WithShared},
26            named::{Named, Renameable, update_context_name},
27        },
28    },
29};
30
31pub mod register;
32
33#[derive(Identifier)]
34pub struct VarnodeId(usize);
35
36/// A named, typed reference to a specific location in a memory [`Space`].
37///
38/// A varnode is identified by its `(space, address, size)` triple. Registers
39/// are modelled as varnodes in the register space; memory operands are varnodes
40/// in the RAM space.
41///
42/// [`Space`]: crate::space::Space
43#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
44pub struct Varnode<'str> {
45    name: Option<Cow<'str, str>>,
46
47    /// An integer label for a generated temporary, used to derive a display name
48    /// (`v{label}`) lazily without allocating a `String` or touching the
49    /// context's name map. Only set when `name` is `None`.
50    label: Option<u32>,
51
52    /// The address of this varnode, in the space it belongs to.
53    address: i64,
54
55    /// The size of this varnode in bytes.
56    size: usize,
57
58    /// The space this varnode belongs to.
59    space: SpaceId,
60}
61
62impl<'str> Varnode<'str> {
63    /// Returns the size of this varnode in bytes, without requiring a context reference.
64    pub fn size_bytes(&self) -> usize {
65        self.size
66    }
67
68    fn new(base: i64, size: usize, space: SpaceId) -> Self {
69        Self {
70            name: None,
71            label: None,
72            address: base,
73            size,
74            space,
75        }
76    }
77
78    /// Creates a new varnode in the context and returns a mutable reference to it.
79    pub fn make<'ctx>(
80        ctx: &'ctx mut Context<'str>,
81        base: i64,
82        size: usize,
83        space: SpaceId,
84    ) -> VarnodeMutRef<'str, 'ctx> {
85        let id = ctx
86            .shared
87            .values
88            .varnodes
89            .push(Varnode::new(base, size, space));
90        VarnodeMutRef::from_id(ctx, id)
91    }
92
93    /// Retrieves an existing varnode by its ID and returns an immutable reference
94    /// to it. Accepts either a `&Context` or a bare `&Shared` (via
95    /// [`AsShared`](crate::value::util::base_ref::AsShared)).
96    pub fn from_id<'ctx>(
97        src: impl crate::value::util::base_ref::AsShared<'ctx, 'str>,
98        id: VarnodeId,
99    ) -> VarnodeRef<'str, 'ctx> {
100        VarnodeRef::from_id(src, id)
101    }
102
103    /// Retrieves an existing varnode from the context by its ID and returns a mutable reference to it.
104    pub fn from_id_mut<'ctx>(
105        ctx: &'ctx mut Context<'str>,
106        id: VarnodeId,
107    ) -> VarnodeMutRef<'str, 'ctx> {
108        VarnodeMutRef::from_id(ctx, id)
109    }
110}
111
112impl<'s, 'ctx: 's, 'str: 'ctx, Ctx> BaseRef<Ctx, VarnodeId>
113where
114    Self: WithShared<'s, 'ctx, 'str>,
115{
116    fn inner(&'s self) -> &'ctx Varnode<'str> {
117        &self.shared().values.varnodes[self.id]
118    }
119
120    fn fmt(&'s self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        if let Some(name) = self.name() {
122            write!(f, "{}", name)
123        } else if let Some(label) = self.inner().label {
124            // Generated temporary: derive its name lazily, no allocation.
125            write!(f, "v{label}")
126        } else {
127            write!(f, "[{}]:{} {}", *self.space(), self.size(), self.address())
128        }
129    }
130
131    /// The space this varnode belongs to.
132    pub fn space(&'s self) -> SpaceRef<'ctx> {
133        Space::from_id(self.shared(), self.inner().space)
134    }
135
136    /// The address at which this varnode begins
137    pub fn address(&'s self) -> i64 {
138        self.inner().address
139    }
140
141    /// The number of bytes in this varnode's range
142    pub fn size(&'s self) -> usize {
143        self.inner().size
144    }
145
146    /// An optional name for this varnode, used for debugging and display purposes.
147    /// The name of a varnode is guaranteed to be unique within the context,
148    /// and renaming a varnode will update the context's name registry to maintain this invariant.
149    pub fn name(&'s self) -> Option<&'ctx str> {
150        self.inner().name.as_deref()
151    }
152
153    /// The integer label of a generated temporary, if any. Temporaries derive
154    /// their display name (`v{label}`) from this without an allocation.
155    pub fn label(&'s self) -> Option<u32> {
156        self.inner().label
157    }
158}
159
160pub type VarnodeRef<'str, 'ctx> = BaseRef<&'ctx Shared<'str>, VarnodeId>;
161
162impl<'s, 'ctx: 's, 'str: 'ctx> WithShared<'s, 'ctx, 'str> for VarnodeRef<'str, 'ctx> {
163    fn shared(&'s self) -> &'ctx Shared<'str> {
164        self.ctx
165    }
166}
167
168impl Named for VarnodeRef<'_, '_> {
169    fn name(&self) -> Option<&str> {
170        self.name()
171    }
172}
173
174impl std::fmt::Display for VarnodeRef<'_, '_> {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        self.fmt(f)
177    }
178}
179
180impl<'str, 'ctx> Value<'str, 'ctx> for VarnodeRef<'str, 'ctx> {
181    fn id(&self) -> ValueId {
182        self.id()
183    }
184
185    fn size(&self) -> usize {
186        self.size()
187    }
188}
189
190pub type VarnodeMutRef<'str, 'ctx> = BaseRef<&'ctx mut Context<'str>, VarnodeId>;
191
192impl<'str, 'ctx> VarnodeMutRef<'str, 'ctx> {
193    fn inner_mut(&mut self) -> &mut Varnode<'str> {
194        &mut self.ctx.shared.values.varnodes[self.id]
195    }
196
197    /// Sets the integer label used to derive a generated temporary's display
198    /// name. Unlike [`Renameable::rename`], this neither allocates nor touches
199    /// the context name map, so it stays off the per-instruction hot path.
200    pub fn set_label(&mut self, label: u32) {
201        self.inner_mut().label = Some(label);
202    }
203}
204
205impl<'s, 'ctx: 's, 'str: 'ctx> WithCtx<'s, 's, 'str> for VarnodeMutRef<'str, 'ctx> {
206    fn ctx(&'s self) -> &'s Context<'str> {
207        self.ctx
208    }
209}
210
211impl<'s, 'ctx: 's, 'str: 'ctx> WithShared<'s, 's, 'str> for VarnodeMutRef<'str, 'ctx> {
212    fn shared(&'s self) -> &'s Shared<'str> {
213        &self.ctx.shared
214    }
215}
216
217impl Named for VarnodeMutRef<'_, '_> {
218    fn name(&self) -> Option<&str> {
219        self.name()
220    }
221}
222
223impl std::fmt::Display for VarnodeMutRef<'_, '_> {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        self.fmt(f)
226    }
227}
228
229impl<'str, 'ctx> Value<'str, 'ctx> for VarnodeMutRef<'str, 'ctx> {
230    fn id(&self) -> ValueId {
231        self.id()
232    }
233
234    fn size(&self) -> usize {
235        self.size()
236    }
237}
238
239impl<'str, 'ctx> Renameable<'str, 'ctx> for VarnodeMutRef<'str, 'ctx> {
240    fn rename(&mut self, name: Cow<'str, str>) -> Result<()> {
241        let id = self.id.into();
242        let old_name = self.inner_mut().name.take();
243        update_context_name(id, self.ctx, name.clone(), old_name.as_deref())?;
244        self.ctx.shared.values.varnodes[self.id].name = Some(name);
245        Ok(())
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use wazabin_qcode_macro::qcode;
252
253    use crate::{
254        context::Context,
255        value::{ModuleView, TempRef},
256    };
257
258    #[test]
259    fn body_local_temp_name() {
260        let mut ctx = Context::new();
261        qcode!(ctx, "<block> varnode i64 ptr; goto <0x1001>;");
262
263        let temp = TempRef::new(ModuleView::new(&ctx), ptr);
264        assert_eq!(temp.name(), Some("ptr"));
265        assert_eq!(temp.space().name(), Some("ptr"));
266    }
267}