coreminer/
variable.rs

1//! # Variable Access Module
2//!
3//! Provides functionality for accessing and manipulating variables in the debugged process.
4//!
5//! This module contains types and methods for reading and writing variables
6//! in the debuggee's memory space, leveraging DWARF debug information to locate
7//! and interpret the variables correctly. It handles the complexities of
8//! different variable storage locations (registers, memory, etc.) and value types.
9//!
10//! Key components:
11//! - [`VariableExpression`]: A type for referring to variables by name
12//! - [`VariableValue`]: An enum representing different forms of variable values
13//! - Methods on the [`Debuggee`] for variable access
14
15use serde::Serialize;
16use tracing::{info, trace};
17
18use crate::dbginfo::{search_through_symbols, OwnedSymbol, SymbolKind};
19use crate::debuggee::Debuggee;
20use crate::dwarf_parse::FrameInfo;
21use crate::errors::{DebuggerError, Result};
22use crate::{get_reg, mem_read, mem_write, set_reg, Addr, Word, WORD_BYTES};
23
24/// A type alias for variable expressions (typically variable names)
25///
26/// [`VariableExpression`] is used to refer to variables in the debugged program,
27/// typically by their source-level names.
28///
29/// In the future, this might include something like dereferencing, or logic, to filter from
30/// various [`OwnedSymbols`][OwnedSymbol].
31pub type VariableExpression = String;
32
33/// Represents a variable value in one of several forms
34///
35/// [`VariableValue`] encapsulates the various ways a variable's value might be
36/// represented, such as raw bytes, numeric values, or machine words. This allows
37/// the debugger to work with variables of different types and sizes.
38///
39/// # Examples
40///
41/// ```
42/// use coreminer::variable::VariableValue;
43/// use gimli::Value;
44///
45/// // Create a variable value from raw bytes
46/// let bytes_value = VariableValue::Bytes(vec![0x01, 0x02, 0x03, 0x04]);
47///
48/// // Create a variable value from a machine word
49/// let word_value = VariableValue::Other(0x123456789);
50///
51/// // Create a variable value from a DWARF numeric value
52/// let numeric_value = VariableValue::Numeric(Value::U32(42));
53///
54/// // Convert a variable value to a u64
55/// let value_as_u64 = bytes_value.to_u64();
56/// ```
57#[derive(Debug, Clone, Serialize)]
58pub enum VariableValue {
59    /// Raw byte representation of a value
60    Bytes(Vec<u8>),
61
62    /// Machine word representation of a value
63    Other(Word),
64
65    /// DWARF numeric value
66    #[serde(serialize_with = "serialize_gimli_value")]
67    Numeric(gimli::Value),
68}
69
70impl VariableValue {
71    /// Returns the size of the variable value in bytes
72    ///
73    /// # Returns
74    ///
75    /// The size of the variable value in bytes
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// use coreminer::variable::VariableValue;
81    /// use gimli::Value;
82    ///
83    /// let bytes_value = VariableValue::Bytes(vec![0x01, 0x02, 0x03, 0x04]);
84    /// assert_eq!(bytes_value.byte_size(), 4);
85    ///
86    /// let word_value = VariableValue::Other(0x123456789);
87    /// assert_eq!(word_value.byte_size(), 8); // Assuming 64-bit (8-byte) words
88    ///
89    /// let numeric_value = VariableValue::Numeric(Value::U32(42));
90    /// assert_eq!(numeric_value.byte_size(), 4);
91    /// ```
92    #[must_use]
93    pub fn byte_size(&self) -> usize {
94        match self {
95            Self::Bytes(b) => b.len(),
96            Self::Other(_w) => WORD_BYTES,
97            Self::Numeric(v) => match v.value_type() {
98                gimli::ValueType::U8 | gimli::ValueType::I8 => 1,
99                gimli::ValueType::U16 | gimli::ValueType::I16 => 2,
100                gimli::ValueType::U32 | gimli::ValueType::I32 | gimli::ValueType::F32 => 4,
101                gimli::ValueType::U64
102                | gimli::ValueType::I64
103                | gimli::ValueType::F64
104                | gimli::ValueType::Generic => 8,
105            },
106        }
107    }
108
109    /// Converts the variable value to a [u64]
110    ///
111    /// # Returns
112    ///
113    /// The variable value as a [u64]
114    ///
115    /// # Panics
116    ///
117    /// This method will panic if [self] is a [`VariableValue::Bytes`] which has more bytes than a [u64] can hold.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use coreminer::variable::VariableValue;
123    /// use gimli::Value;
124    ///
125    /// let bytes_value = VariableValue::Bytes(vec![0x42, 0x00, 0x00, 0x00]);
126    /// assert_eq!(bytes_value.to_u64(), 0x42);
127    ///
128    /// let word_value = VariableValue::Other(0x123456789);
129    /// assert_eq!(word_value.to_u64(), 0x123456789);
130    ///
131    /// let numeric_value = VariableValue::Numeric(Value::U32(42));
132    /// assert_eq!(numeric_value.to_u64(), 42);
133    /// ```
134    #[must_use]
135    pub fn to_u64(&self) -> u64 {
136        match self {
137            Self::Bytes(b) => {
138                assert!((b.len() <= WORD_BYTES), "too many bytes to put into a word");
139                // NOTE: this is safe because `b` should never have more bytes than a u64
140                crate::bytes_to_u64(b).unwrap()
141            }
142            Self::Other(w) => crate::bytes_to_u64(&w.to_ne_bytes()).unwrap(),
143            Self::Numeric(v) => match v {
144                gimli::Value::U8(v) => (*v).into(),
145                gimli::Value::I8(v) => crate::bytes_to_u64(&v.to_ne_bytes()).unwrap(),
146                gimli::Value::U16(v) => (*v).into(),
147                gimli::Value::I16(v) => crate::bytes_to_u64(&v.to_ne_bytes()).unwrap(),
148                gimli::Value::U32(v) => (*v).into(),
149                gimli::Value::I32(v) => crate::bytes_to_u64(&v.to_ne_bytes()).unwrap(),
150                gimli::Value::F32(v) => crate::bytes_to_u64(&v.to_ne_bytes()).unwrap(),
151                gimli::Value::U64(v) | gimli::Value::Generic(v) => *v,
152                gimli::Value::I64(v) => crate::bytes_to_u64(&v.to_ne_bytes()).unwrap(),
153                gimli::Value::F64(v) => crate::bytes_to_u64(&v.to_ne_bytes()).unwrap(),
154            },
155        }
156    }
157
158    /// Resizes a value to the specified number of bytes
159    ///
160    /// # Parameters
161    ///
162    /// * `target_size` - The target size in bytes
163    ///
164    /// # Returns
165    ///
166    /// A vector of bytes with the specified size
167    ///
168    /// # Panics
169    ///
170    /// This method will panic if [self] is a [`VariableValue::Bytes`] which has more bytes than a [u64] can hold.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use coreminer::variable::VariableValue;
176    /// use gimli::Value;
177    ///
178    /// let value = VariableValue::Other(0x123456789);
179    ///
180    /// // Resize to 4 bytes
181    /// let bytes = value.resize_to_bytes(4);
182    /// assert_eq!(bytes, vec![0x89, 0x67, 0x45, 0x23]);
183    ///
184    /// // Resize to 2 bytes
185    /// let bytes = value.resize_to_bytes(2);
186    /// assert_eq!(bytes, vec![0x89, 0x67]);
187    /// ```
188    #[must_use]
189    pub fn resize_to_bytes(&self, target_size: usize) -> Vec<u8> {
190        assert!(
191            (target_size <= WORD_BYTES),
192            "requested byte size was larger than a word"
193        );
194
195        let mut data = self.to_u64().to_ne_bytes().to_vec();
196        data.truncate(target_size);
197        data
198    }
199}
200
201impl From<usize> for VariableValue {
202    fn from(value: usize) -> Self {
203        VariableValue::Numeric(gimli::Value::Generic(value as u64))
204    }
205}
206impl From<gimli::Value> for VariableValue {
207    fn from(value: gimli::Value) -> Self {
208        Self::Numeric(value)
209    }
210}
211
212impl Debuggee {
213    /// Filters variable expressions to find matching symbols
214    ///
215    /// # Parameters
216    ///
217    /// * `haystack` - The symbols to search through
218    /// * `expression` - The variable expression to match
219    ///
220    /// # Returns
221    ///
222    /// * `Ok(Vec<OwnedSymbol>)` - The matching symbols
223    /// * `Err(DebuggerError)` - If filtering failed
224    ///
225    /// # Errors
226    ///
227    /// This function can fail if there are issues with the symbol table.
228    pub fn filter_expressions(
229        &self,
230        haystack: &[OwnedSymbol],
231        expression: &VariableExpression,
232    ) -> Result<Vec<OwnedSymbol>> {
233        Ok(search_through_symbols(haystack, |s| {
234            s.name() == Some(expression)
235        }))
236    }
237
238    /// Checks if a symbol is a valid variable
239    ///
240    /// # Parameters
241    ///
242    /// * `sym` - The symbol to check
243    ///
244    /// # Returns
245    ///
246    /// * `Ok(())` - If the symbol is a valid variable
247    /// * `Err(DebuggerError)` - If the symbol is not a valid variable
248    ///
249    /// # Errors
250    ///
251    /// This function fails if:
252    /// - The symbol is not a variable or parameter
253    /// - The symbol does not have a data type
254    /// - The symbol does not have a location
255    ///
256    /// If it fails, the symbol should not be used for either [`Self::var_write`] or [`Self::var_read`].
257    fn check_sym_variable_ok(sym: &OwnedSymbol) -> Result<()> {
258        match sym.kind() {
259            SymbolKind::Variable | SymbolKind::Parameter => (),
260            _ => return Err(DebuggerError::WrongSymbolKind(sym.kind())),
261        }
262        if sym.datatype().is_none() {
263            return Err(DebuggerError::VariableSymbolNoType);
264        }
265        if sym.location().is_none() {
266            return Err(DebuggerError::SymbolHasNoLocation);
267        }
268        Ok(())
269    }
270
271    /// Writes a value to a variable
272    ///
273    /// Prefer to use the more high level [`crate::debugger::Debugger::write_variable`].
274    ///
275    /// # Parameters
276    ///
277    /// * `sym` - The symbol representing the variable
278    /// * `frame_info` - Stack frame information
279    /// * `value` - The value to write
280    ///
281    /// # Returns
282    ///
283    /// * `Ok(())` - If the write was successful
284    /// * `Err(DebuggerError)` - If the write failed
285    ///
286    /// # Errors
287    ///
288    /// This function can fail if:
289    /// - The symbol is not a valid variable
290    /// - The variable's location cannot be determined
291    /// - Memory or register access fails
292    /// - The data type of the variable cannot be determined
293    ///
294    /// # Examples
295    ///
296    /// ```no_run
297    /// use coreminer::debuggee::Debuggee;
298    /// use coreminer::dwarf_parse::FrameInfo;
299    /// use coreminer::addr::Addr;
300    /// use coreminer::variable::VariableValue;
301    ///
302    /// # fn example(debuggee: &Debuggee, sym: &coreminer::dbginfo::OwnedSymbol) -> coreminer::errors::Result<()> {
303    /// // Create frame information
304    /// // Calculate these with the ELF and DWARF information
305    /// let frame_info = FrameInfo::new(
306    ///     Some(Addr::from(0x7fffffffe000usize)),
307    ///     Some(Addr::from(0x7fffffffe010usize))
308    /// );
309    ///
310    /// // Write the value 42 to the variable
311    /// debuggee.var_write(sym, &frame_info, &VariableValue::from(42usize))?;
312    /// # Ok(())
313    /// # }
314    /// ```
315    pub fn var_write(
316        &self,
317        sym: &OwnedSymbol,
318        frame_info: &FrameInfo,
319        value: &VariableValue,
320    ) -> Result<()> {
321        Debuggee::check_sym_variable_ok(sym)?;
322        let Some(datatype) = self.get_type_for_symbol(sym)? else {
323            return Err(DebuggerError::NoDatatypeFound);
324        };
325
326        let Some(loc_attr) = sym.location() else {
327            return Err(DebuggerError::SymbolHasNoLocation);
328        };
329        let location = self.parse_location(loc_attr, frame_info, sym.encoding())?;
330
331        match location {
332            gimli::Location::Address { address } => {
333                let Some(byte_size) = datatype.byte_size() else {
334                    return Err(DebuggerError::SymbolHasNoByteSize);
335                };
336                let value_raw = value.resize_to_bytes(byte_size);
337                let addr: Addr = address.into();
338                trace!("writing to {addr}");
339                let _written = mem_write(&value_raw, self.pid, addr)?;
340            }
341            gimli::Location::Register { register } => {
342                set_reg(self.pid, register.try_into()?, value.to_u64())?;
343            }
344            other => unimplemented!(
345                "writing to variable with gimli location of type {other:?} is not implemented"
346            ),
347        }
348
349        Ok(())
350    }
351
352    /// Reads the value of a variable
353    ///
354    /// Prefer to use the more high level [`crate::debugger::Debugger::read_variable`].
355    ///
356    /// # Parameters
357    ///
358    /// * `sym` - The symbol representing the variable
359    /// * `frame_info` - Stack frame information
360    ///
361    /// # Returns
362    ///
363    /// * `Ok(VariableValue)` - The variable's value
364    /// * `Err(DebuggerError)` - If the read failed
365    ///
366    /// # Errors
367    ///
368    /// This function can fail if:
369    /// - The symbol is not a valid variable
370    /// - The variable's location cannot be determined
371    /// - Memory or register access fails
372    /// - The data type of the variable cannot be determined
373    ///
374    /// # Examples
375    ///
376    /// ```no_run
377    /// use coreminer::debuggee::Debuggee;
378    /// use coreminer::dwarf_parse::FrameInfo;
379    /// use coreminer::addr::Addr;
380    ///
381    /// # fn example(debuggee: &Debuggee, sym: &coreminer::dbginfo::OwnedSymbol) -> coreminer::errors::Result<()> {
382    /// // Create frame information
383    /// // Calculate these with the ELF and DWARF information
384    /// let frame_info = FrameInfo::new(
385    ///     Some(Addr::from(0x7fffffffe000usize)),
386    ///     Some(Addr::from(0x7fffffffe010usize))
387    /// );
388    ///
389    /// // Read the variable's value
390    /// let value = debuggee.var_read(sym, &frame_info)?;
391    /// println!("Variable value: {:?}", value);
392    /// # Ok(())
393    /// # }
394    /// ```
395    pub fn var_read(&self, sym: &OwnedSymbol, frame_info: &FrameInfo) -> Result<VariableValue> {
396        Debuggee::check_sym_variable_ok(sym)?;
397        let Some(datatype) = self.get_type_for_symbol(sym)? else {
398            return Err(DebuggerError::NoDatatypeFound);
399        };
400
401        let Some(loc_attr) = sym.location() else {
402            return Err(DebuggerError::SymbolHasNoLocation);
403        };
404        let location = self.parse_location(loc_attr, frame_info, sym.encoding())?;
405
406        let value = match location {
407            gimli::Location::Value { value } => value.into(),
408            gimli::Location::Bytes { value } => VariableValue::Bytes(value.to_vec()),
409            gimli::Location::Address { address } => {
410                let addr: Addr = address.into();
411                info!("reading var from {addr}");
412                let Some(size) = datatype.byte_size() else {
413                    return Err(DebuggerError::SymbolHasNoByteSize);
414                };
415                let mut buf = vec![0; size];
416                let _len = mem_read(&mut buf, self.pid, addr)?;
417
418                VariableValue::Bytes(buf)
419            }
420            gimli::Location::Register { register } => {
421                VariableValue::Other(get_reg(self.pid, register.try_into()?)? as Word)
422            }
423            other => unimplemented!("gimli location of type {other:?} is not implemented"),
424        };
425
426        Ok(value)
427    }
428}
429
430fn serialize_gimli_value<S>(
431    value: &gimli::Value,
432    serializer: S,
433) -> std::result::Result<S::Ok, S::Error>
434where
435    S: serde::Serializer,
436{
437    match value {
438        gimli::Value::U8(v) => serializer.serialize_u8(*v),
439        gimli::Value::I8(v) => serializer.serialize_i8(*v),
440        gimli::Value::U16(v) => serializer.serialize_u16(*v),
441        gimli::Value::I16(v) => serializer.serialize_i16(*v),
442        gimli::Value::U32(v) => serializer.serialize_u32(*v),
443        gimli::Value::I32(v) => serializer.serialize_i32(*v),
444        gimli::Value::U64(v) | gimli::Value::Generic(v) => serializer.serialize_u64(*v),
445        gimli::Value::I64(v) => serializer.serialize_i64(*v),
446        gimli::Value::F32(v) => serializer.serialize_f32(*v),
447        gimli::Value::F64(v) => serializer.serialize_f64(*v),
448    }
449}
450
451#[cfg(test)]
452mod test {
453    use super::*;
454
455    #[test]
456    fn test_variable_value_sizing() {
457        let v = VariableValue::Numeric(gimli::Value::U8(42));
458        assert_eq!(v.byte_size(), 1);
459        assert_eq!(v.to_u64(), 42);
460
461        let v = VariableValue::Numeric(gimli::Value::I8(42));
462        assert_eq!(v.byte_size(), 1);
463        assert_eq!(v.to_u64(), 42);
464
465        let v = VariableValue::Numeric(gimli::Value::U16(42));
466        assert_eq!(v.byte_size(), 2);
467        assert_eq!(v.to_u64(), 42);
468
469        let v = VariableValue::Numeric(gimli::Value::I16(42));
470        assert_eq!(v.byte_size(), 2);
471        assert_eq!(v.to_u64(), 42);
472
473        let v = VariableValue::Numeric(gimli::Value::U32(42));
474        assert_eq!(v.byte_size(), 4);
475        assert_eq!(v.to_u64(), 42);
476
477        let v = VariableValue::Numeric(gimli::Value::I32(42));
478        assert_eq!(v.byte_size(), 4);
479        assert_eq!(v.to_u64(), 42);
480
481        let v = VariableValue::Numeric(gimli::Value::U64(42));
482        assert_eq!(v.byte_size(), 8);
483        assert_eq!(v.to_u64(), 42);
484
485        let v = VariableValue::Numeric(gimli::Value::I64(42));
486        assert_eq!(v.byte_size(), 8);
487        assert_eq!(v.to_u64(), 42);
488
489        let v = VariableValue::Numeric(gimli::Value::F32(42.19));
490        assert_eq!(v.byte_size(), 4);
491
492        let v = VariableValue::Numeric(gimli::Value::F64(42.19));
493        assert_eq!(v.byte_size(), 8);
494
495        let v = VariableValue::Other(19);
496        assert_eq!(v.byte_size(), 8);
497        assert_eq!(v.to_u64(), 19);
498
499        let v = VariableValue::Bytes(vec![0x19, 19, 19]);
500        assert_eq!(v.byte_size(), 3);
501        assert_eq!(v.to_u64(), 1_250_073);
502    }
503
504    #[test]
505    fn test_resize_bytes() {
506        let v = VariableValue::Other(42);
507        let bytes = v.resize_to_bytes(4);
508        assert_eq!(bytes.len(), 4);
509        assert_eq!(bytes, vec![42, 0, 0, 0]);
510
511        let bytes = v.resize_to_bytes(8);
512        assert_eq!(bytes.len(), 8);
513        assert_eq!(bytes, vec![42, 0, 0, 0, 0, 0, 0, 0]);
514
515        let bytes = v.resize_to_bytes(1);
516        assert_eq!(bytes.len(), 1);
517        assert_eq!(bytes, vec![42]);
518    }
519
520    #[test]
521    fn test_signed_integer_to_bytes() {
522        let v: i8 = 19;
523        let b = v.to_ne_bytes();
524        assert_eq!(b.len(), 1);
525        assert_eq!(b, [19]);
526
527        let v: i32 = 19;
528        let b = v.to_ne_bytes();
529        assert_eq!(b.len(), 4);
530        assert_eq!(b, [19, 0, 0, 0]);
531
532        let v: i32 = -19;
533        let b = v.to_ne_bytes();
534        assert_eq!(b.len(), 4);
535        assert_eq!(b, [237, 255, 255, 255]);
536    }
537}