pub mod attributes;
pub mod evaluate;
use crate::call_stack::MemoryAccess;
use crate::registers::Registers;
use anyhow::{anyhow, Result};
use evaluate::{convert_to_gimli_value, BaseTypeValue, EvaluatorValue};
use gimli::{
AttributeValue::UnitRef,
DebuggingInformationEntry, DieReference, Dwarf, Evaluation, EvaluationResult,
EvaluationResult::{
Complete, RequiresAtLocation, RequiresBaseType, RequiresCallFrameCfa, RequiresEntryValue,
RequiresFrameBase, RequiresIndexedAddress, RequiresMemory, RequiresParameterRef,
RequiresRegister, RequiresRelocatedAddress, RequiresTls,
},
Expression, Reader, Unit, UnitOffset,
};
use log::{error, info};
use std::convert::TryInto;
pub fn call_evaluate<R: Reader<Offset = usize>, T: MemoryAccess>(
dwarf: &Dwarf<R>,
pc: u32,
expr: gimli::Expression<R>,
frame_base: Option<u64>,
unit: &Unit<R>,
die: &DebuggingInformationEntry<R>,
registers: &Registers,
mem: &mut T,
) -> Result<EvaluatorValue<R>> {
if let Ok(Some(tattr)) = die.attr_value(gimli::DW_AT_type) {
match tattr {
gimli::AttributeValue::UnitRef(offset) => {
let die = unit.entry(offset)?;
return evaluate(
dwarf,
unit,
pc,
expr,
frame_base,
Some(unit),
Some(&die),
registers,
mem,
);
}
gimli::AttributeValue::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 type_unit = dwarf.unit(header)?;
if let Some(offset) = offset.to_unit_offset(&type_unit) {
let die = type_unit.entry(offset)?;
return evaluate(
dwarf,
unit,
pc,
expr,
frame_base,
Some(&type_unit),
Some(&die),
registers,
mem,
);
}
}
error!("Unreachable");
return Err(anyhow!("Unreachable"));
}
attribute => {
error!("Unimplemented for attribute {:?}", attribute);
return Err(anyhow!("Unimplemented for attribute {:?}", attribute));
}
};
} else if let Ok(Some(die_offset)) = die.attr_value(gimli::DW_AT_abstract_origin) {
match die_offset {
UnitRef(offset) => {
if let Ok(ndie) = unit.entry(offset) {
return call_evaluate(dwarf, pc, expr, frame_base, unit, &ndie, registers, mem);
}
}
_ => {
error!("Unimplemented");
return Err(anyhow!("Unimplemented"));
}
};
}
error!("Unreachable");
Err(anyhow!("Unreachable"))
}
pub fn evaluate<R: Reader<Offset = usize>, T: MemoryAccess>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
pc: u32,
expr: Expression<R>,
frame_base: Option<u64>,
type_unit: Option<&gimli::Unit<R>>,
type_die: Option<&gimli::DebuggingInformationEntry<'_, '_, R>>,
registers: &Registers,
mem: &mut T,
) -> Result<EvaluatorValue<R>> {
let pieces = evaluate_pieces(dwarf, unit, pc, expr, frame_base, registers, mem)?;
info!("Got pieces");
evaluate_value(dwarf, pieces, type_unit, type_die, registers, mem)
}
pub fn evaluate_value<R: Reader<Offset = usize>, T: MemoryAccess>(
dwarf: &Dwarf<R>,
pieces: Vec<gimli::Piece<R>>,
type_unit: Option<&gimli::Unit<R>>,
type_die: Option<&gimli::DebuggingInformationEntry<'_, '_, R>>,
registers: &Registers,
mem: &mut T,
) -> Result<EvaluatorValue<R>> {
match type_unit {
Some(unit) => match type_die {
Some(die) => {
info!("with type info");
return EvaluatorValue::evaluate_variable_with_type(
dwarf,
registers,
mem,
&pieces,
unit.header.offset(),
die.offset(),
);
}
None => (),
},
None => (),
};
info!("without type info");
EvaluatorValue::evaluate_variable(registers, mem, &pieces)
}
pub fn evaluate_pieces<R: Reader<Offset = usize>, T: MemoryAccess>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
pc: u32,
expr: Expression<R>,
frame_base: Option<u64>,
registers: &Registers,
mem: &mut T,
) -> Result<Vec<gimli::Piece<R>>> {
let mut eval = expr.evaluation(unit.encoding());
let mut result = eval.evaluate()?;
loop {
match result {
Complete => break,
RequiresMemory {
address,
size,
space: _, base_type,
} => match mem.get_address(&(address as u32), size as usize) {
Some(data) => {
let value = eval_base_type(unit, data, base_type)?;
result = eval.resume_with_memory(convert_to_gimli_value(value))?;
}
None => {
return Err(anyhow!("Requires Memory"));
}
},
RequiresRegister {
register,
base_type,
} => match registers.get_register_value(®ister.0) {
Some(data) => {
let bytes = data.to_le_bytes().to_vec();
let value = eval_base_type(unit, bytes, base_type)?;
result = eval.resume_with_register(convert_to_gimli_value(value))?;
}
None => {
return Err(anyhow!("Requires register {}", register.0));
}
},
RequiresFrameBase => {
result = eval.resume_with_frame_base(match frame_base {
Some(val) => val,
None => {
error!("Requires frame base");
return Err(anyhow!("Requires frame base"));
}
})?;
}
RequiresTls(_tls) => {
error!("Unimplemented");
return Err(anyhow!("Unimplemented")); }
RequiresCallFrameCfa => {
result = eval.resume_with_call_frame_cfa(
registers.cfa.ok_or_else(|| anyhow!("Requires CFA"))? as u64,
)?;
}
RequiresAtLocation(die_ref) => match die_ref {
DieReference::UnitRef(unit_offset) => help_at_location(
dwarf,
unit,
pc,
&mut eval,
&mut result,
frame_base,
unit_offset,
registers,
mem,
)?,
DieReference::DebugInfoRef(debug_info_offset) => {
let unit_header = dwarf.debug_info.header_from_offset(debug_info_offset)?;
if let Some(unit_offset) = debug_info_offset.to_unit_offset(&unit_header) {
let new_unit = dwarf.unit(unit_header)?;
help_at_location(
dwarf,
&new_unit,
pc,
&mut eval,
&mut result,
frame_base,
unit_offset,
registers,
mem,
)?;
} else {
return Err(anyhow!("Could not find at location"));
}
}
},
RequiresEntryValue(entry) => {
let entry_value = evaluate(
dwarf, unit, pc, entry, frame_base, None, None, registers, mem,
)?;
result = eval.resume_with_entry_value(convert_to_gimli_value(match entry_value
.to_value()
{
Some(val) => val,
None => {
error!("Optimized Out");
return Err(anyhow!("Optimized Out"));
}
}))?;
}
RequiresParameterRef(unit_offset) => {
let die = unit.entry(unit_offset)?;
let call_value = match die.attr_value(gimli::DW_AT_call_value)? {
Some(val) => val,
None => {
error!("Could not find required paramter");
return Err(anyhow!("Could not find required parameter"));
}
};
let expr = match call_value.exprloc_value() {
Some(val) => val,
None => {
error!("Could not find required paramter");
return Err(anyhow!("Could not find required parameter"));
}
};
let value = evaluate(
dwarf,
unit,
pc,
expr,
frame_base,
Some(unit),
Some(&die),
registers,
mem,
)?;
if let EvaluatorValue::Value(BaseTypeValue::U64(val), _) = value {
result = eval.resume_with_parameter_ref(val)?;
} else {
error!("Could not find required paramter");
return Err(anyhow!("Could not find required parameter"));
}
}
RequiresRelocatedAddress(_num) => {
error!("Unimplemented");
return Err(anyhow!("Unimplemented"));
}
RequiresIndexedAddress {
index: _,
relocate: _,
} => {
error!("Unimplemented");
return Err(anyhow!("Unimplemented"));
}
RequiresBaseType(unit_offset) => {
let die = unit.entry(unit_offset)?;
let mut attrs = die.attrs();
while let Some(attr) = match attrs.next() {
Ok(val) => val,
Err(err) => {
error!("{:?}", err);
return Err(anyhow!("{:?}", err));
}
} {
println!("Attribute name = {:?}", attr.name());
println!("Attribute value = {:?}", attr.value());
}
error!("Unimplemented");
return Err(anyhow!("Unimplemented"));
}
};
}
Ok(eval.result())
}
fn eval_base_type<R>(
unit: &gimli::Unit<R>,
data: Vec<u8>,
base_type: gimli::UnitOffset<usize>,
) -> Result<BaseTypeValue>
where
R: Reader<Offset = usize>,
{
if base_type.0 == 0 {
let value = match data.len() {
0 => 0,
1 => u8::from_le_bytes(match data.try_into() {
Ok(val) => val,
Err(err) => {
error!("{:?}", err);
return Err(anyhow!("{:?}", err));
}
}) as u64,
2 => u16::from_le_bytes(match data.try_into() {
Ok(val) => val,
Err(err) => {
error!("{:?}", err);
return Err(anyhow!("{:?}", err));
}
}) as u64,
4 => u32::from_le_bytes(match data.try_into() {
Ok(val) => val,
Err(err) => {
error!("{:?}", err);
return Err(anyhow!("{:?}", err));
}
}) as u64,
8 => u64::from_le_bytes(match data.try_into() {
Ok(val) => val,
Err(err) => {
error!("{:?}", err);
return Err(anyhow!("{:?}", err));
}
}),
_ => {
error!("Unreachable");
return Err(anyhow!("Unreachable"));
}
};
return Ok(BaseTypeValue::Generic(value));
}
let die = unit.entry(base_type)?;
if die.tag() != gimli::DW_TAG_base_type {
error!("Requires at the die has tag DW_TAG_base_type");
return Err(anyhow!("Requires at the die has tag DW_TAG_base_type"));
}
let encoding = match die.attr_value(gimli::DW_AT_encoding)? {
Some(gimli::AttributeValue::Encoding(dwate)) => dwate,
_ => {
error!("Expected base type die to have attribute DW_AT_encoding");
return Err(anyhow!(
"Expected base type die to have attribute DW_AT_encoding"
));
}
};
BaseTypeValue::parse_base_type(data, encoding)
}
fn help_at_location<R: Reader<Offset = usize>, T: MemoryAccess>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
pc: u32,
eval: &mut Evaluation<R>,
result: &mut EvaluationResult<R>,
frame_base: Option<u64>,
unit_offset: UnitOffset<usize>,
registers: &Registers,
mem: &mut T,
) -> Result<()>
where
R: Reader<Offset = usize>,
{
let die = unit.entry(unit_offset)?;
let location = match die.attr_value(gimli::DW_AT_location)? {
Some(val) => val,
None => {
error!("Could not find location attribute");
return Err(anyhow!("Could not find location attribute"));
}
};
if let Some(expr) = location.exprloc_value() {
let val = call_evaluate(dwarf, pc, expr, frame_base, unit, &die, registers, mem)?;
if let EvaluatorValue::Bytes(b) = val {
*result = eval.resume_with_at_location(b)?;
Ok(())
} else {
error!("Error expected bytes");
Err(anyhow!("Error expected bytes"))
}
} else {
error!("die has no at location");
Err(anyhow!("die has no at location"))
}
}