rust_debug/
variable.rs

1use gimli::{
2    AttributeValue::{DebugInfoRef, DebugStrRef, Exprloc, LocationListsRef, UnitRef},
3    DebuggingInformationEntry, Dwarf, Reader, Unit, UnitOffset, UnitSectionOffset,
4};
5
6use crate::evaluate::attributes;
7use crate::evaluate::evaluate;
8use crate::registers::Registers;
9use crate::source_information::SourceInformation;
10use crate::utils::in_range;
11use crate::variable::evaluate::EvaluatorValue;
12use crate::{call_stack::MemoryAccess, utils::DwarfOffset};
13use anyhow::{anyhow, Result};
14use log::{error, info, trace};
15
16/// Defines what debug information a variable has.
17#[derive(Debug, Clone)]
18pub struct Variable<R: Reader<Offset = usize>> {
19    /// The name of the variable.
20    pub name: Option<String>,
21
22    /// A tree structured like the variable type in DWARF, but it also contains the values
23    pub value: EvaluatorValue<R>,
24
25    /// The source code location where the variable was declared.
26    pub source: Option<SourceInformation>,
27}
28
29impl<R: Reader<Offset = usize>> Variable<R> {
30    /// Retrieve the variables debug information.
31    ///
32    /// Description:
33    ///
34    /// * `dwarf` - A reference to gimli-rs `Dwarf` struct.
35    /// * `registers` - A reference to the `Registers` struct.
36    /// * `memory` - A reference to a struct that implements the `MemoryAccess` trait.
37    /// * `section_offset` - A offset to the compilation unit where the DIE for the variable is
38    /// located.
39    /// * `unit_offset` - A offset into the compilation unit where the DIE representing the
40    /// variable is located.
41    /// * `frame_base` - The value of the frame base, which is often needed to evaluate the
42    /// variable.
43    /// * `cwd` - The work directory of the program being debugged.
44    ///
45    /// This function will go through the DIE in the compilation unit to find the necessary
46    /// debug information.
47    /// Then it will use that information to evaluate the value of the variable.
48    pub fn get_variable<M: MemoryAccess>(
49        dwarf: &Dwarf<R>,
50        registers: &Registers,
51        memory: &mut M,
52        dwarf_offset: DwarfOffset,
53        frame_base: Option<u64>,
54        cwd: &str,
55    ) -> Result<Variable<R>> {
56        // Get the program counter.
57        let pc: u32 = *registers
58            .get_register_value(
59                &(registers
60                    .program_counter_register
61                    .ok_or_else(|| anyhow!("Requires that the program counter register is known"))?
62                    as u16),
63            )
64            .ok_or_else(|| anyhow!("Requires that the program counter registers has a value"))?;
65
66        // Get the variable die.
67        let header = dwarf.debug_info.header_from_offset(
68            match dwarf_offset.section_offset.as_debug_info_offset() {
69                Some(val) => val,
70                None => {
71                    error!("Could not convert section offset into debug info offset");
72                    return Err(anyhow!(
73                        "Could not convert section offset into debug info offset"
74                    ));
75                }
76            },
77        )?;
78        let unit = gimli::Unit::new(dwarf, header)?;
79        let die = unit.entry(dwarf_offset.unit_offset)?;
80
81        let name = get_var_name(dwarf, &unit, &die)?;
82        info!("name: {:?}", name);
83
84        // Get the source code location the variable was declared.
85        let source = match find_variable_source_information(dwarf, &unit, &die, cwd) {
86            Ok(source) => Some(source),
87            Err(_) => None,
88        };
89        info!("has source");
90
91        let expression = match find_variable_location(dwarf, &unit, &die, pc)? {
92            VariableLocation::Expression(expr) => {
93                trace!("VariableLocation::Expression");
94                expr
95            }
96            VariableLocation::LocationListEntry(llent) => {
97                trace!("VariableLocation::LocationListEntry");
98                llent.data
99            }
100            VariableLocation::LocationOutOfRange => {
101                trace!("VariableLocation::LocationOutOfRange");
102                return Ok(Variable {
103                    name,
104                    value: EvaluatorValue::LocationOutOfRange,
105                    source,
106                });
107            }
108            VariableLocation::NoLocation => {
109                trace!("VariableLocation::NoLocation");
110                return Ok(Variable {
111                    name,
112                    value: EvaluatorValue::OptimizedOut,
113                    source,
114                });
115            }
116        };
117        info!("has expression");
118
119        let (type_section_offset, type_unit_offset) = find_variable_type_die(dwarf, &unit, &die)?;
120        info!("type sec offset: {:?}", type_section_offset);
121        info!("type unit offset: {:?}", type_unit_offset);
122        let header = dwarf.debug_info.header_from_offset(
123            match type_section_offset.as_debug_info_offset() {
124                Some(val) => val,
125                None => {
126                    error!("Could not convert section offset into debug info offset");
127                    return Err(anyhow!(
128                        "Could not convert section offset into debug info offset"
129                    ));
130                }
131            },
132        )?;
133        let type_unit = gimli::Unit::new(dwarf, header)?;
134        let type_die = type_unit.entry(type_unit_offset)?;
135
136        info!("has type");
137
138        let value = evaluate(
139            dwarf,
140            &unit,
141            pc,
142            expression,
143            frame_base,
144            Some(&type_unit),
145            Some(&type_die),
146            registers,
147            memory,
148        )?;
149
150        Ok(Variable {
151            name,
152            value,
153            source,
154        })
155    }
156}
157
158/// Will check if the given DIE has one of the DWARF tags that represents a variable.
159///
160/// Description:
161///
162/// * `die` - A reference to DIE.
163///
164/// Will check if the given type has one of the following tags:
165/// - DW_TAG_variable
166/// - DW_TAG_formal_parameter
167/// - DW_TAG_constant
168/// If the DIE has one of the tags the function will return `true`, otherwise `false`.
169pub fn is_variable_die<R: Reader<Offset = usize>>(die: &DebuggingInformationEntry<R>) -> bool {
170    // Check that it is a variable.
171    die.tag() == gimli::DW_TAG_variable
172        || die.tag() == gimli::DW_TAG_formal_parameter
173        || die.tag() == gimli::DW_TAG_constant
174}
175
176/// Will retrieve the name of a variable DIE.
177///
178/// Description:
179///
180/// * `dwarf` - A reference to gimli-rs `Dwarf` struct.
181/// * `unit` - A reference to gimli-rs `Unit` struct, which the given DIE is located in.
182/// * `die` - A reference to DIE.
183///
184/// Will check if the given DIE represents a variable, if it does not it will return a error.
185/// After that it will try to evaluate the `DW_AT_name` attribute and return the result.
186/// But if it dose not have the name attribute it will try to get the name from the DIE in the
187/// `DW_AT_abstract_origin` attribute.
188/// If that attribute is missing it will return `Ok(None)`, because the variable does not have a
189/// name.
190pub fn get_var_name<R: Reader<Offset = usize>>(
191    dwarf: &Dwarf<R>,
192    unit: &Unit<R>,
193    die: &DebuggingInformationEntry<R>,
194) -> Result<Option<String>> {
195    if is_variable_die(die) {
196        // Get the name of the variable.
197        if let Ok(Some(DebugStrRef(offset))) = die.attr_value(gimli::DW_AT_name) {
198            return Ok(Some(dwarf.string(offset)?.to_string()?.to_string()));
199        } else if let Ok(Some(offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
200            match offset {
201                UnitRef(o) => {
202                    if let Ok(ndie) = unit.entry(o) {
203                        return get_var_name(dwarf, unit, &ndie);
204                    }
205                }
206                DebugInfoRef(di_offset) => {
207                    let offset = gimli::UnitSectionOffset::DebugInfoOffset(di_offset);
208                    let mut iter = dwarf.debug_info.units();
209                    while let Ok(Some(header)) = iter.next() {
210                        let unit = dwarf.unit(header)?;
211                        if let Some(offset) = offset.to_unit_offset(&unit) {
212                            if let Ok(ndie) = unit.entry(offset) {
213                                return get_var_name(dwarf, &unit, &ndie);
214                            }
215                        }
216                    }
217                    return Ok(None);
218                }
219                val => {
220                    error!("Unimplemented for {:?}", val);
221                    return Err(anyhow!("Unimplemented for {:?}", val));
222                }
223            };
224        }
225
226        Ok(None)
227    } else {
228        Err(anyhow!("This die is not a variable"))
229    }
230}
231
232/// Holds the location of a variable.
233pub enum VariableLocation<R: Reader<Offset = usize>> {
234    /// The gimli-rs expression that describes the location of the variable.
235    Expression(gimli::Expression<R>),
236
237    /// The gimli-rs location list entry that describes the location of the Variable.
238    LocationListEntry(gimli::LocationListEntry<R>),
239
240    /// The variable has no location currently but had or will have one. Note that the location can
241    /// be a constant stored in the DWARF stack.
242    LocationOutOfRange,
243
244    /// The variable has no location.
245    NoLocation,
246}
247
248/// Find the location of a variable.
249///
250/// Description:
251///
252/// * `dwarf` - A reference to gimli-rs `Dwarf` struct.
253/// * `unit` - A reference to gimli-rs `Unit` struct which contains the given DIE.
254/// * `die` - A reference to the variables DIE that contains the location.
255/// * `address` - A address that will be used to find the location, this is most often the current machine code address.
256///
257/// Will get the location for the given address from the attribute `DW_AT_location` in the variable DIE.
258pub fn find_variable_location<R: Reader<Offset = usize>>(
259    dwarf: &Dwarf<R>,
260    unit: &Unit<R>,
261    die: &DebuggingInformationEntry<R>,
262    address: u32,
263) -> Result<VariableLocation<R>> {
264    if is_variable_die(die) {
265        match die.attr_value(gimli::DW_AT_location)? {
266            Some(Exprloc(expr)) => Ok(VariableLocation::Expression(expr)),
267            Some(LocationListsRef(offset)) => {
268                let mut locations = dwarf.locations(unit, offset)?;
269                let mut count = 0;
270                while let Some(llent) = locations.next()? {
271                    if in_range(address, &llent.range) {
272                        return Ok(VariableLocation::LocationListEntry(llent));
273                    }
274                    count += 1;
275                }
276
277                if count > 0 {
278                    Ok(VariableLocation::LocationOutOfRange)
279                } else {
280                    Ok(VariableLocation::NoLocation)
281                }
282            }
283            None => Ok(VariableLocation::NoLocation),
284            Some(v) => {
285                error!("Unimplemented for {:?}", v);
286                Err(anyhow!("Unimplemented for {:?}", v))
287            }
288        }
289    } else {
290        Err(anyhow!("This die is not a variable"))
291    }
292}
293
294/// Find the DIE representing the type of a variable.
295///
296/// Description:
297///
298/// * `dwarf` - A reference to gimli-rs `Dwarf` struct.
299/// * `unit` - A reference to gimli-rs `Unit` struct, which the given DIE is located in.
300/// * `die` - A reference to the DIE representing a variable, which the resulting type DIE will represent the type off..
301///
302/// Will find the DIE representing the type of the variable that the given DIE represents.
303/// The type DIE is found using the attribute `DW_AT_type` in the given DIE or in the DIE from the
304/// attribute `DW_AT_abstract_origin`.
305pub fn find_variable_type_die<R: Reader<Offset = usize>>(
306    dwarf: &Dwarf<R>,
307    unit: &Unit<R>,
308    die: &DebuggingInformationEntry<R>,
309) -> Result<(UnitSectionOffset, UnitOffset)> {
310    if is_variable_die(die) {
311        match attributes::type_attribute(dwarf, unit, die)? {
312            Some(result) => Ok(result),
313            None => {
314                if let Ok(Some(die_offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
315                    match die_offset {
316                        UnitRef(offset) => {
317                            if let Ok(ao_die) = unit.entry(offset) {
318                                return find_variable_type_die(dwarf, unit, &ao_die);
319                            }
320                        }
321                        DebugInfoRef(di_offset) => {
322                            let offset = gimli::UnitSectionOffset::DebugInfoOffset(di_offset);
323                            let mut iter = dwarf.debug_info.units();
324                            while let Ok(Some(header)) = iter.next() {
325                                let unit = dwarf.unit(header)?;
326                                if let Some(offset) = offset.to_unit_offset(&unit) {
327                                    if let Ok(ndie) = unit.entry(offset) {
328                                        return find_variable_type_die(dwarf, &unit, &ndie);
329                                    }
330                                }
331                            }
332                            return Err(anyhow!("Could not find this variables type die"));
333                        }
334                        val => {
335                            error!("Unimplemented for {:?}", val);
336                            return Err(anyhow!("Unimplemented for {:?}", val));
337                        }
338                    };
339                }
340
341                Err(anyhow!("Could not find this variables type die"))
342            }
343        }
344    } else {
345        Err(anyhow!("This die is not a variable"))
346    }
347}
348
349/// Retrieve the variables source location where it was declared.
350///
351/// Description:
352///
353/// * `dwarf` - A reference to gimli-rs `Dwarf` struct.
354/// * `unit` - A reference to gimli-rs `Unit` struct, which the given DIE is located in.
355/// * `die` - A reference to DIE.
356/// * `cwd` - The work directory of the debugged program.
357///
358/// This function will retrieve the source code location where the variable was declared.
359/// The information is retrieved from the  attributes starting with `DW_AT_decl_` in the given DIE,
360/// or in the DIE found in the attribute `DW_AT_abstract_origin`.
361pub fn find_variable_source_information<R: Reader<Offset = usize>>(
362    dwarf: &Dwarf<R>,
363    unit: &Unit<R>,
364    die: &DebuggingInformationEntry<R>,
365    cwd: &str,
366) -> Result<SourceInformation> {
367    if is_variable_die(die) {
368        if let Ok(Some(die_offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
369            match die_offset {
370                UnitRef(offset) => {
371                    let ao_die = unit.entry(offset)?;
372                    find_variable_source_information(dwarf, unit, &ao_die, cwd)
373                }
374                DebugInfoRef(di_offset) => {
375                    let offset = gimli::UnitSectionOffset::DebugInfoOffset(di_offset);
376                    let mut iter = dwarf.debug_info.units();
377                    while let Ok(Some(header)) = iter.next() {
378                        let unit = dwarf.unit(header)?;
379                        if let Some(offset) = offset.to_unit_offset(&unit) {
380                            if let Ok(ndie) = unit.entry(offset) {
381                                return find_variable_source_information(dwarf, &unit, &ndie, cwd);
382                            }
383                        }
384                    }
385                    Err(anyhow!("Could not find this variables die"))
386                }
387                val => {
388                    error!("Unimplemented for {:?}", val);
389                    Err(anyhow!("Unimplemented for {:?}", val))
390                }
391            }
392        } else {
393            SourceInformation::get_die_source_information(dwarf, unit, die, cwd)
394        }
395    } else {
396        Err(anyhow!("This die is not a variable"))
397    }
398}