tasru/extract.rs
1use crate::GimliReader;
2use gimli::{Endianity, EvaluationResult, Location};
3
4#[derive(Debug)]
5pub enum ExtractError {
6 WarnAndContinue { message: String },
7 GimliError(gimli::Error),
8 UnknownVariable,
9}
10
11impl From<gimli::Error> for ExtractError {
12 fn from(value: gimli::Error) -> Self {
13 ExtractError::GimliError(value)
14 }
15}
16
17/// The result of `UnitInfo::evaluate_expression()` can be the value of a variable, or a memory location.
18#[derive(Debug)]
19pub(crate) enum ExpressionResult {
20 #[allow(dead_code)]
21 Value(u64),
22 Location(VariableLocation),
23}
24
25// /// A [Variable] will have either a valid value, or some reason why a value could not be constructed.
26// /// - If we encounter expected errors, they will be displayed to the user as defined below.
27// /// - If we encounter unexpected errors, they will be treated as proper errors and will propagated
28// /// to the calling process as an `Err()`
29// #[derive(Clone, Debug, PartialEq, Eq, Default)]
30// pub enum VariableValue {
31// /// A valid value of this variable
32// Valid(String),
33// /// Notify the user that we encountered a problem correctly resolving the variable.
34// /// - The variable will be visible to the user, as will the other field of the variable.
35// /// - The contained warning message will be displayed to the user.
36// /// - The debugger will not attempt to resolve additional fields or children of this variable.
37// Error(String),
38// /// The value has not been set. This could be because ...
39// /// - It is too early in the process to have discovered its value, or ...
40// /// - The variable cannot have a stored value, e.g. a `struct`. In this case, please use
41// /// `Variable::get_value` to infer a human readable value from the value of the struct's fields.
42// #[default]
43// Empty,
44// }
45
46// impl std::fmt::Display for VariableValue {
47// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48// match self {
49// VariableValue::Valid(value) => value.fmt(f),
50// VariableValue::Error(error) => write!(f, "< {error} >"),
51// VariableValue::Empty => write!(
52// f,
53// "Value not set. Please use Variable::get_value() to infer a human readable variable value"
54// ),
55// }
56// }
57// }
58
59// impl VariableValue {
60// /// Returns `true` if the variable resolver did not encounter an error, `false` otherwise.
61// pub fn is_valid(&self) -> bool {
62// !matches!(self, VariableValue::Error(_))
63// }
64
65// /// Returns `true` if no value or error is present, `false` otherwise.
66// pub fn is_empty(&self) -> bool {
67// matches!(self, VariableValue::Empty)
68// }
69// }
70
71#[derive(Debug, Clone, PartialEq, Eq, Default)]
72pub enum VariableLocation {
73 /// Location of the variable is not known. This means that it has not been evaluated yet.
74 #[default]
75 Unknown,
76 /// The variable does not have a location currently, probably due to optimisations.
77 Unavailable,
78 /// The variable can be found in memory, at this address.
79 Address(u64),
80 /// The value of the variable is directly available.
81 Value,
82 /// There was an error evaluating the variable location.
83 Error(String),
84 /// Support for handling the location of this variable is not (yet) implemented.
85 Unsupported(String),
86}
87
88impl VariableLocation {
89 /// Return the memory address, if available. Otherwise an error is returned.
90 pub fn memory_address(&self) -> Result<u64, ExtractError> {
91 match self {
92 VariableLocation::Address(address) => Ok(*address),
93 other => Err(ExtractError::WarnAndContinue {
94 message: format!("Variable does not have a memory location: location={other:?}"),
95 }),
96 }
97 }
98
99 /// Check if the location is valid, ie. not an error, unsupported, or unavailable.
100 pub fn valid(&self) -> bool {
101 match self {
102 VariableLocation::Address(_) | VariableLocation::Value | VariableLocation::Unknown => {
103 true
104 }
105 _other => false,
106 }
107 }
108}
109
110impl std::fmt::Display for VariableLocation {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 match self {
113 VariableLocation::Unknown => "<unknown value>".fmt(f),
114 VariableLocation::Unavailable => "<value not available>".fmt(f),
115 VariableLocation::Address(address) => write!(f, "{address:#010X}"),
116 VariableLocation::Value => "<not applicable - statically stored value>".fmt(f),
117 VariableLocation::Error(error) => error.fmt(f),
118 VariableLocation::Unsupported(reason) => reason.fmt(f),
119 }
120 }
121}
122
123// /// If a DW_AT_byte_size attribute exists, return the u64 value, otherwise (including errors) return None
124// pub(crate) fn extract_byte_size(node_die: &DebuggingInformationEntry<GimliReader>) -> Option<u64> {
125// match node_die.attr(gimli::DW_AT_byte_size) {
126// Ok(Some(byte_size_attr)) => match byte_size_attr.value() {
127// AttributeValue::Udata(byte_size) => Some(byte_size),
128// AttributeValue::Data1(byte_size) => Some(byte_size as u64),
129// AttributeValue::Data2(byte_size) => Some(byte_size as u64),
130// AttributeValue::Data4(byte_size) => Some(byte_size as u64),
131// AttributeValue::Data8(byte_size) => Some(byte_size),
132// other => {
133// eprintln!("Unimplemented: DW_AT_byte_size value: {other:?}");
134// None
135// }
136// },
137// Ok(None) => None,
138// Err(error) => {
139// eprintln!(
140// "Failed to extract byte_size: {error:?} for debug_entry {:?}",
141// node_die.tag().static_string()
142// );
143// None
144// }
145// }
146// }
147
148// pub(crate) fn extract_line(attribute_value: AttributeValue<GimliReader>) -> Option<u64> {
149// match attribute_value {
150// AttributeValue::Udata(line) => Some(line),
151// _ => None,
152// }
153// }
154
155// /// If file information is available, it returns `Some(directory:PathBuf, file_name:String)`, otherwise `None`.
156// pub(crate) fn extract_file(
157// // _debug_info: &DebugInfo,
158// _unit: &gimli::Unit<GimliReader>,
159// _attribute_value: AttributeValue<GimliReader>,
160// ) -> Option<(TypedPathBuf, String)> {
161// // match attribute_value {
162// // AttributeValue::FileIndex(index) => {
163// // if let Some((Some(file), Some(path))) = debug_info.find_file_and_directory(unit, index)
164// // {
165// // Some((path, file))
166// // } else {
167// // eprintln!("Unable to extract file or path from {:?}.", attribute_value);
168// // None
169// // }
170// // }
171// // other => {
172// // eprintln!(
173// // "Unable to extract file information from attribute value {:?}: Not implemented.",
174// // other
175// // );
176// // None
177// // }
178// // }
179// None
180// }
181
182/// Tries to get the result of a DWARF expression in the form of a Piece.
183pub(crate) fn expression_to_piece<ENDIAN: Endianity>(
184 expression: gimli::Expression<GimliReader<ENDIAN>>,
185 encoding: gimli::Encoding,
186) -> Result<Vec<gimli::Piece<GimliReader<ENDIAN>, usize>>, ExtractError> {
187 let mut evaluation = expression.evaluation(encoding);
188 let mut result = evaluation.evaluate()?;
189
190 loop {
191 result = match result {
192 EvaluationResult::Complete => return Ok(evaluation.result()),
193 // EvaluationResult::RequiresMemory { address, size, .. } => {
194 // read_memory(size, memory, address, &mut evaluation)?
195 // }
196 // EvaluationResult::RequiresFrameBase => {
197 // provide_frame_base(frame_info.frame_base, &mut evaluation)?
198 // }
199 // EvaluationResult::RequiresRegister {
200 // register,
201 // base_type,
202 // } => provide_register(frame_info.registers, register, base_type, &mut evaluation)?,
203 EvaluationResult::RequiresRelocatedAddress(address_index) => {
204 // The address_index as an offset from 0, so just pass it into the next step.
205 evaluation.resume_with_relocated_address(address_index)?
206 }
207 // EvaluationResult::RequiresCallFrameCfa => {
208 // provide_cfa(frame_info.canonical_frame_address, &mut evaluation)?
209 // }
210 unimplemented_expression => {
211 return Err(ExtractError::WarnAndContinue {
212 message: format!(
213 "Unimplemented: Expressions that include {unimplemented_expression:?} are not currently supported."
214 ),
215 });
216 }
217 }
218 }
219}
220
221/// Evaluate a [`gimli::Expression`] as a valid memory location.
222/// Return values are implemented as follows:
223/// - `Result<_, ExtractError>`: This happens when we encounter an error we did not expect, and will propagate upwards until the debugger request is failed. NOT GRACEFUL, and should be avoided.
224/// - `Result<ExpressionResult::Value(),_>`: The value is statically stored in the binary, and can be returned, and has no relevant memory location.
225/// - `Result<ExpressionResult::Location(),_>`: One of the variants of VariableLocation, and needs to be interpreted for handling the 'expected' errors we encounter during evaluation.
226pub(crate) fn evaluate_expression<ENDIAN: Endianity>(
227 expression: gimli::Expression<GimliReader<ENDIAN>>,
228 encoding: gimli::Encoding,
229) -> Result<ExpressionResult, ExtractError> {
230 fn evaluate_address(address: u64) -> ExpressionResult {
231 let location = if address >= u32::MAX as u64
232 /*&& !memory.supports_native_64bit_access()*/
233 {
234 VariableLocation::Error(format!(
235 "The memory location for this variable value ({:#010X}) is invalid. Please report this as a bug.",
236 address
237 ))
238 } else {
239 VariableLocation::Address(address)
240 };
241 ExpressionResult::Location(location)
242 }
243
244 let pieces = expression_to_piece(expression, encoding)?;
245
246 if pieces.is_empty() {
247 return Ok(ExpressionResult::Location(VariableLocation::Error(
248 "Error: expr_to_piece() returned 0 results".to_string(),
249 )));
250 }
251 if pieces.len() > 1 {
252 return Ok(ExpressionResult::Location(VariableLocation::Error(
253 "<unsupported memory implementation>".to_string(),
254 )));
255 }
256
257 let result = match &pieces[0].location {
258 Location::Empty => {
259 // This means the value was optimized away.
260 ExpressionResult::Location(VariableLocation::Unavailable)
261 }
262 Location::Address { address: 0 } => {
263 let error = "The value of this variable may have been optimized out of the debug info, by the compiler.".to_string();
264 ExpressionResult::Location(VariableLocation::Error(error))
265 }
266 Location::Address { address } => evaluate_address(*address),
267 Location::Value { value } => value.to_u64(u64::MAX).map(ExpressionResult::Value)?,
268 // Location::Register { register } => {
269 // if let Some(address) = frame_info
270 // .registers
271 // .get_register_by_dwarf_id(register.0)
272 // .and_then(|register| register.value)
273 // {
274 // match address.try_into() {
275 // Ok(address) => evaluate_address(address),
276 // Err(error) => ExpressionResult::Location(VariableLocation::Error(format!(
277 // "Error: Cannot convert register value to location address: {error:?}"
278 // ))),
279 // }
280 // } else {
281 // ExpressionResult::Location(VariableLocation::Error(format!(
282 // "Error: Cannot resolve register: {register:?}"
283 // )))
284 // }
285 // }
286 l => ExpressionResult::Location(VariableLocation::Error(format!(
287 "Unimplemented: extract_location() found a location type: {:.100}",
288 format!("{l:?}")
289 ))),
290 };
291
292 Ok(result)
293}
294
295// /// - Find the location using either DW_AT_location, DW_AT_data_member_location, or DW_AT_frame_base attribute.
296// ///
297// /// Return values are implemented as follows:
298// /// - `Result<_, ExtractError>`: This happens when we encounter an error we did not expect, and will propagate upwards until the debugger request is failed. **NOT GRACEFUL**, and should be avoided.
299// /// - `Result<ExpressionResult::Value(),_>`: The value is statically stored in the binary, and can be returned, and has no relevant memory location.
300// /// - `Result<ExpressionResult::Location(),_>`: One of the variants of VariableLocation, and needs to be interpreted for handling the 'expected' errors we encounter during evaluation.
301// pub(crate) fn extract_location(
302// debug_info: &DebugInfo,
303// node_die: &gimli::DebuggingInformationEntry<GimliReader>,
304// parent_location: &VariableLocation,
305// memory: &mut dyn MemoryInterface,
306// frame_info: StackFrameInfo<'_>,
307// ) -> Result<ExpressionResult, ExtractError> {
308// trait ResultExt {
309// /// Turns UnwindIncompleteResults into Unavailable locations
310// fn convert_incomplete(self) -> Result<ExpressionResult, ExtractError>;
311// }
312
313// impl ResultExt for Result<ExpressionResult, ExtractError> {
314// fn convert_incomplete(self) -> Result<ExpressionResult, ExtractError> {
315// match self {
316// Ok(result) => Ok(result),
317// Err(ExtractError::WarnAndContinue { message }) => {
318// tracing::warn!("UnwindIncompleteResults: {:?}", message);
319// Ok(ExpressionResult::Location(VariableLocation::Unavailable))
320// }
321// e => e,
322// }
323// }
324// }
325
326// let mut attrs = node_die.attrs();
327// while let Ok(Some(attr)) = attrs.next() {
328// let result = match attr.name() {
329// gimli::DW_AT_location
330// | gimli::DW_AT_frame_base
331// | gimli::DW_AT_data_member_location => match attr.value() {
332// gimli::AttributeValue::Exprloc(expression) => evaluate_expression(memory, expression, frame_info)
333// .convert_incomplete()?,
334
335// gimli::AttributeValue::Udata(offset_from_location) => {
336// let location = if let VariableLocation::Address(address) = parent_location {
337// let Some(location) = address.checked_add(offset_from_location) else {
338// return Err(ExtractError::WarnAndContinue {
339// message: "Overflow calculating variable address"
340// .to_string(),
341// });
342// };
343
344// VariableLocation::Address(location)
345// } else {
346// parent_location.clone()
347// };
348
349// ExpressionResult::Location(location)
350// }
351
352// gimli::AttributeValue::LocationListsRef(location_list_offset) => self
353// .evaluate_location_list_ref(
354// debug_info,
355// location_list_offset,
356// frame_info,
357// memory,
358// )
359// .convert_incomplete()?,
360
361// other_attribute_value => {
362// ExpressionResult::Location(VariableLocation::Unsupported(format!(
363// "Unimplemented: extract_location() Could not extract location from: {:.100}",
364// format!("{other_attribute_value:?}")
365// )))
366// }
367// },
368
369// gimli::DW_AT_address_class => {
370// let location = match attr.value() {
371// gimli::AttributeValue::AddressClass(gimli::DwAddr(0)) => {
372// // We pass on the location of the parent, which will later to be used along with DW_AT_data_member_location to calculate the location of this variable.
373// parent_location.clone()
374// }
375// gimli::AttributeValue::AddressClass(address_class) => {
376// VariableLocation::Unsupported(format!(
377// "Unimplemented: extract_location() found unsupported DW_AT_address_class(gimli::DwAddr({address_class:?}))"
378// ))
379// }
380// other_attribute_value => {
381// VariableLocation::Unsupported(format!(
382// "Unimplemented: extract_location() found invalid DW_AT_address_class: {:.100}",
383// format!("{other_attribute_value:?}")
384// ))
385// }
386// };
387
388// ExpressionResult::Location(location)
389// }
390
391// _other_attributes => {
392// // These will be handled elsewhere.
393// continue;
394// }
395// };
396
397// return Ok(result);
398// }
399
400// // If we get here, we did not find a location attribute, then leave the value as Unknown.
401// Ok(ExpressionResult::Location(VariableLocation::Unknown))
402// }
403
404// pub(crate) fn extract_location(
405// attrs: &[gimli::Attribute<GimliReader>],
406// unit: gimli::UnitRef<GimliReader>,
407// ) -> Result<ExpressionResult, ExtractError> {
408// trait ResultExt {
409// /// Turns UnwindIncompleteResults into Unavailable locations
410// fn convert_incomplete(self) -> Result<ExpressionResult, ExtractError>;
411// }
412
413// impl ResultExt for Result<ExpressionResult, ExtractError> {
414// fn convert_incomplete(self) -> Result<ExpressionResult, ExtractError> {
415// match self {
416// Ok(result) => Ok(result),
417// Err(ExtractError::WarnAndContinue { .. }) => {
418// // tracing::warn!("UnwindIncompleteResults: {:?}", message);
419// Ok(ExpressionResult::Location(VariableLocation::Unavailable))
420// }
421// e => e,
422// }
423// }
424// }
425
426// for attr in attrs {
427// let result = match attr.name() {
428// gimli::DW_AT_location | gimli::DW_AT_frame_base | gimli::DW_AT_data_member_location => {
429// match attr.value() {
430// gimli::AttributeValue::Exprloc(expression) => {
431// evaluate_expression(expression, unit.unit.encoding())
432// .convert_incomplete()?
433// }
434
435// // gimli::AttributeValue::Udata(offset_from_location) => {
436// // let location = if let VariableLocation::Address(address) = parent_location {
437// // let Some(location) = address.checked_add(offset_from_location) else {
438// // return Err(ExtractError::WarnAndContinue {
439// // message: "Overflow calculating variable address".to_string(),
440// // });
441// // };
442
443// // VariableLocation::Address(location)
444// // } else {
445// // parent_location.clone()
446// // };
447
448// // ExpressionResult::Location(location)
449// // }
450
451// // gimli::AttributeValue::LocationListsRef(location_list_offset) => self
452// // .evaluate_location_list_ref(
453// // debug_info,
454// // location_list_offset,
455// // frame_info,
456// // memory,
457// // )
458// // .convert_incomplete()?,
459// other_attribute_value => {
460// ExpressionResult::Location(VariableLocation::Unsupported(format!(
461// "Unimplemented: extract_location() Could not extract location from: {:.100}",
462// format!("{other_attribute_value:?}")
463// )))
464// }
465// }
466// }
467
468// gimli::DW_AT_address_class => {
469// let location = match attr.value() {
470// // gimli::AttributeValue::AddressClass(gimli::DwAddr(0)) => {
471// // // We pass on the location of the parent, which will later to be used along with DW_AT_data_member_location to calculate the location of this variable.
472// // parent_location.clone()
473// // }
474// gimli::AttributeValue::AddressClass(address_class) => {
475// VariableLocation::Unsupported(format!(
476// "Unimplemented: extract_location() found unsupported DW_AT_address_class(gimli::DwAddr({address_class:?}))"
477// ))
478// }
479// other_attribute_value => {
480// VariableLocation::Unsupported(format!(
481// "Unimplemented: extract_location() found invalid DW_AT_address_class: {:.100}",
482// format!("{other_attribute_value:?}")
483// ))
484// }
485// };
486
487// ExpressionResult::Location(location)
488// }
489
490// _other_attributes => {
491// // These will be handled elsewhere.
492// continue;
493// }
494// };
495// return Ok(result);
496// }
497// Ok(ExpressionResult::Location(VariableLocation::Unknown))
498// }