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#[derive(Debug, Clone)]
18pub struct Variable<R: Reader<Offset = usize>> {
19 pub name: Option<String>,
21
22 pub value: EvaluatorValue<R>,
24
25 pub source: Option<SourceInformation>,
27}
28
29impl<R: Reader<Offset = usize>> Variable<R> {
30 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 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 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 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
158pub fn is_variable_die<R: Reader<Offset = usize>>(die: &DebuggingInformationEntry<R>) -> bool {
170 die.tag() == gimli::DW_TAG_variable
172 || die.tag() == gimli::DW_TAG_formal_parameter
173 || die.tag() == gimli::DW_TAG_constant
174}
175
176pub 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 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
232pub enum VariableLocation<R: Reader<Offset = usize>> {
234 Expression(gimli::Expression<R>),
236
237 LocationListEntry(gimli::LocationListEntry<R>),
239
240 LocationOutOfRange,
243
244 NoLocation,
246}
247
248pub 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
294pub 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
349pub 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}