use gimli::{
AttributeValue::{DebugInfoRef, DebugStrRef, Exprloc, LocationListsRef, UnitRef},
DebuggingInformationEntry, Dwarf, Reader, Unit, UnitOffset, UnitSectionOffset,
};
use crate::evaluate::attributes;
use crate::evaluate::evaluate;
use crate::registers::Registers;
use crate::source_information::SourceInformation;
use crate::utils::in_range;
use crate::variable::evaluate::EvaluatorValue;
use crate::{call_stack::MemoryAccess, utils::DwarfOffset};
use anyhow::{anyhow, Result};
use log::{error, info, trace};
#[derive(Debug, Clone)]
pub struct Variable<R: Reader<Offset = usize>> {
pub name: Option<String>,
pub value: EvaluatorValue<R>,
pub source: Option<SourceInformation>,
}
impl<R: Reader<Offset = usize>> Variable<R> {
pub fn get_variable<M: MemoryAccess>(
dwarf: &Dwarf<R>,
registers: &Registers,
memory: &mut M,
dwarf_offset: DwarfOffset,
frame_base: Option<u64>,
cwd: &str,
) -> Result<Variable<R>> {
let pc: u32 = *registers
.get_register_value(
&(registers
.program_counter_register
.ok_or_else(|| anyhow!("Requires that the program counter register is known"))?
as u16),
)
.ok_or_else(|| anyhow!("Requires that the program counter registers has a value"))?;
let header = dwarf.debug_info.header_from_offset(
match dwarf_offset.section_offset.as_debug_info_offset() {
Some(val) => val,
None => {
error!("Could not convert section offset into debug info offset");
return Err(anyhow!(
"Could not convert section offset into debug info offset"
));
}
},
)?;
let unit = gimli::Unit::new(dwarf, header)?;
let die = unit.entry(dwarf_offset.unit_offset)?;
let name = get_var_name(dwarf, &unit, &die)?;
info!("name: {:?}", name);
let source = match find_variable_source_information(dwarf, &unit, &die, cwd) {
Ok(source) => Some(source),
Err(_) => None,
};
info!("has source");
let expression = match find_variable_location(dwarf, &unit, &die, pc)? {
VariableLocation::Expression(expr) => {
trace!("VariableLocation::Expression");
expr
}
VariableLocation::LocationListEntry(llent) => {
trace!("VariableLocation::LocationListEntry");
llent.data
}
VariableLocation::LocationOutOfRange => {
trace!("VariableLocation::LocationOutOfRange");
return Ok(Variable {
name,
value: EvaluatorValue::LocationOutOfRange,
source,
});
}
VariableLocation::NoLocation => {
trace!("VariableLocation::NoLocation");
return Ok(Variable {
name,
value: EvaluatorValue::OptimizedOut,
source,
});
}
};
info!("has expression");
let (type_section_offset, type_unit_offset) = find_variable_type_die(dwarf, &unit, &die)?;
info!("type sec offset: {:?}", type_section_offset);
info!("type unit offset: {:?}", type_unit_offset);
let header = dwarf.debug_info.header_from_offset(
match type_section_offset.as_debug_info_offset() {
Some(val) => val,
None => {
error!("Could not convert section offset into debug info offset");
return Err(anyhow!(
"Could not convert section offset into debug info offset"
));
}
},
)?;
let type_unit = gimli::Unit::new(dwarf, header)?;
let type_die = type_unit.entry(type_unit_offset)?;
info!("has type");
let value = evaluate(
dwarf,
&unit,
pc,
expression,
frame_base,
Some(&type_unit),
Some(&type_die),
registers,
memory,
)?;
Ok(Variable {
name,
value,
source,
})
}
}
pub fn is_variable_die<R: Reader<Offset = usize>>(die: &DebuggingInformationEntry<R>) -> bool {
die.tag() == gimli::DW_TAG_variable
|| die.tag() == gimli::DW_TAG_formal_parameter
|| die.tag() == gimli::DW_TAG_constant
}
pub fn get_var_name<R: Reader<Offset = usize>>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
die: &DebuggingInformationEntry<R>,
) -> Result<Option<String>> {
if is_variable_die(die) {
if let Ok(Some(DebugStrRef(offset))) = die.attr_value(gimli::DW_AT_name) {
return Ok(Some(dwarf.string(offset)?.to_string()?.to_string()));
} else if let Ok(Some(offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
match offset {
UnitRef(o) => {
if let Ok(ndie) = unit.entry(o) {
return get_var_name(dwarf, unit, &ndie);
}
}
DebugInfoRef(di_offset) => {
let offset = gimli::UnitSectionOffset::DebugInfoOffset(di_offset);
let mut iter = dwarf.debug_info.units();
while let Ok(Some(header)) = iter.next() {
let unit = dwarf.unit(header)?;
if let Some(offset) = offset.to_unit_offset(&unit) {
if let Ok(ndie) = unit.entry(offset) {
return get_var_name(dwarf, &unit, &ndie);
}
}
}
return Ok(None);
}
val => {
error!("Unimplemented for {:?}", val);
return Err(anyhow!("Unimplemented for {:?}", val));
}
};
}
Ok(None)
} else {
Err(anyhow!("This die is not a variable"))
}
}
pub enum VariableLocation<R: Reader<Offset = usize>> {
Expression(gimli::Expression<R>),
LocationListEntry(gimli::LocationListEntry<R>),
LocationOutOfRange,
NoLocation,
}
pub fn find_variable_location<R: Reader<Offset = usize>>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
die: &DebuggingInformationEntry<R>,
address: u32,
) -> Result<VariableLocation<R>> {
if is_variable_die(die) {
match die.attr_value(gimli::DW_AT_location)? {
Some(Exprloc(expr)) => Ok(VariableLocation::Expression(expr)),
Some(LocationListsRef(offset)) => {
let mut locations = dwarf.locations(unit, offset)?;
let mut count = 0;
while let Some(llent) = locations.next()? {
if in_range(address, &llent.range) {
return Ok(VariableLocation::LocationListEntry(llent));
}
count += 1;
}
if count > 0 {
Ok(VariableLocation::LocationOutOfRange)
} else {
Ok(VariableLocation::NoLocation)
}
}
None => Ok(VariableLocation::NoLocation),
Some(v) => {
error!("Unimplemented for {:?}", v);
Err(anyhow!("Unimplemented for {:?}", v))
}
}
} else {
Err(anyhow!("This die is not a variable"))
}
}
pub fn find_variable_type_die<R: Reader<Offset = usize>>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
die: &DebuggingInformationEntry<R>,
) -> Result<(UnitSectionOffset, UnitOffset)> {
if is_variable_die(die) {
match attributes::type_attribute(dwarf, unit, die)? {
Some(result) => Ok(result),
None => {
if let Ok(Some(die_offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
match die_offset {
UnitRef(offset) => {
if let Ok(ao_die) = unit.entry(offset) {
return find_variable_type_die(dwarf, unit, &ao_die);
}
}
DebugInfoRef(di_offset) => {
let offset = gimli::UnitSectionOffset::DebugInfoOffset(di_offset);
let mut iter = dwarf.debug_info.units();
while let Ok(Some(header)) = iter.next() {
let unit = dwarf.unit(header)?;
if let Some(offset) = offset.to_unit_offset(&unit) {
if let Ok(ndie) = unit.entry(offset) {
return find_variable_type_die(dwarf, &unit, &ndie);
}
}
}
return Err(anyhow!("Could not find this variables type die"));
}
val => {
error!("Unimplemented for {:?}", val);
return Err(anyhow!("Unimplemented for {:?}", val));
}
};
}
Err(anyhow!("Could not find this variables type die"))
}
}
} else {
Err(anyhow!("This die is not a variable"))
}
}
pub fn find_variable_source_information<R: Reader<Offset = usize>>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
die: &DebuggingInformationEntry<R>,
cwd: &str,
) -> Result<SourceInformation> {
if is_variable_die(die) {
if let Ok(Some(die_offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
match die_offset {
UnitRef(offset) => {
let ao_die = unit.entry(offset)?;
find_variable_source_information(dwarf, unit, &ao_die, cwd)
}
DebugInfoRef(di_offset) => {
let offset = gimli::UnitSectionOffset::DebugInfoOffset(di_offset);
let mut iter = dwarf.debug_info.units();
while let Ok(Some(header)) = iter.next() {
let unit = dwarf.unit(header)?;
if let Some(offset) = offset.to_unit_offset(&unit) {
if let Ok(ndie) = unit.entry(offset) {
return find_variable_source_information(dwarf, &unit, &ndie, cwd);
}
}
}
Err(anyhow!("Could not find this variables die"))
}
val => {
error!("Unimplemented for {:?}", val);
Err(anyhow!("Unimplemented for {:?}", val))
}
}
} else {
SourceInformation::get_die_source_information(dwarf, unit, die, cwd)
}
} else {
Err(anyhow!("This die is not a variable"))
}
}