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}