pub mod debug_info;
pub mod debug_step;
pub mod function_die;
pub(crate) mod language;
pub mod registers;
pub(crate) mod source_instructions;
pub mod stack_frame;
pub mod unit_info;
pub mod variable;
pub mod variable_cache;
pub(crate) mod exception_handling;
pub use self::{
debug_info::*, debug_step::SteppingMode, registers::*, source_instructions::SourceLocation,
source_instructions::VerifiedBreakpoint, stack_frame::StackFrame, variable::*,
variable_cache::VariableCache,
};
use crate::{core::Core, MemoryInterface};
use gimli::AttributeValue;
use gimli::DebuggingInformationEntry;
use gimli::EvaluationResult;
use serde::Serialize;
use typed_path::TypedPathBuf;
use std::num::ParseIntError;
use std::{
io,
num::NonZeroU32,
str::Utf8Error,
sync::atomic::{AtomicU32, Ordering},
vec,
};
pub type EndianReader = gimli::EndianReader<gimli::LittleEndian, std::rc::Rc<[u8]>>;
#[derive(Debug, thiserror::Error)]
pub enum DebugError {
#[error("IO Error while accessing debug data")]
Io(#[from] io::Error),
#[error("Error accessing debug data")]
DebugData(#[from] object::read::Error),
#[error("Error parsing debug data")]
Parse(#[from] gimli::read::Error),
#[error("Non-UTF8 data found in debug data")]
NonUtf8(#[from] Utf8Error),
#[error("Error using the probe")]
Probe(#[from] crate::Error),
#[error(transparent)]
CharConversion(#[from] std::char::CharTryFromError),
#[error(transparent)]
IntConversion(#[from] std::num::TryFromIntError),
#[error("{message}")]
WarnAndContinue {
message: String,
},
#[error("Not implemented: {0}")]
NotImplemented(&'static str),
#[error("{0}")]
Other(String),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
pub enum ColumnType {
LeftEdge,
Column(u64),
}
impl From<gimli::ColumnType> for ColumnType {
fn from(column: gimli::ColumnType) -> Self {
match column {
gimli::ColumnType::LeftEdge => ColumnType::LeftEdge,
gimli::ColumnType::Column(c) => ColumnType::Column(c.get()),
}
}
}
impl From<u64> for ColumnType {
fn from(column: u64) -> Self {
match column {
0 => ColumnType::LeftEdge,
_ => ColumnType::Column(column),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ObjectRef {
Valid(NonZeroU32),
#[default]
Invalid,
}
impl PartialOrd for ObjectRef {
fn partial_cmp(&self, other: &ObjectRef) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ObjectRef {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
i64::from(*self).cmp(&i64::from(*other))
}
}
impl From<ObjectRef> for i64 {
fn from(value: ObjectRef) -> Self {
match value {
ObjectRef::Valid(v) => v.get() as i64,
ObjectRef::Invalid => 0,
}
}
}
impl From<i64> for ObjectRef {
fn from(value: i64) -> Self {
if value > 0 {
ObjectRef::Valid(NonZeroU32::try_from(value as u32).unwrap())
} else {
ObjectRef::Invalid
}
}
}
impl std::str::FromStr for ObjectRef {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = s.parse::<i64>()?;
Ok(ObjectRef::from(value))
}
}
static CACHE_KEY: AtomicU32 = AtomicU32::new(1);
pub fn get_object_reference() -> ObjectRef {
let key = CACHE_KEY.fetch_add(1, Ordering::SeqCst);
ObjectRef::Valid(NonZeroU32::new(key).unwrap())
}
fn extract_file(
debug_info: &DebugInfo,
unit: &gimli::Unit<GimliReader>,
attribute_value: AttributeValue<GimliReader>,
) -> Option<TypedPathBuf> {
match attribute_value {
AttributeValue::FileIndex(index) => {
if let Some(path) = debug_info.find_file_and_directory(unit, index) {
Some(path)
} else {
tracing::warn!("Unable to extract file or path from {:?}.", attribute_value);
None
}
}
other => {
tracing::warn!(
"Unable to extract file information from attribute value {:?}: Not implemented.",
other
);
None
}
}
}
fn extract_byte_size(node_die: &DebuggingInformationEntry<GimliReader>) -> Option<u64> {
match node_die.attr(gimli::DW_AT_byte_size) {
Ok(Some(byte_size_attr)) => match byte_size_attr.value() {
AttributeValue::Udata(byte_size) => Some(byte_size),
AttributeValue::Data1(byte_size) => Some(byte_size as u64),
AttributeValue::Data2(byte_size) => Some(byte_size as u64),
AttributeValue::Data4(byte_size) => Some(byte_size as u64),
AttributeValue::Data8(byte_size) => Some(byte_size),
other => {
tracing::warn!("Unimplemented: DW_AT_byte_size value: {other:?}");
None
}
},
Ok(None) => None,
Err(error) => {
tracing::warn!(
"Failed to extract byte_size: {error:?} for debug_entry {:?}",
node_die.tag().static_string()
);
None
}
}
}
fn extract_line(attribute_value: AttributeValue<GimliReader>) -> Option<u64> {
match attribute_value {
AttributeValue::Udata(line) => Some(line),
_ => None,
}
}
#[allow(clippy::unwrap_used, clippy::expect_used)]
pub(crate) fn _print_all_attributes(
core: &mut Core<'_>,
stackframe_cfa: Option<u64>,
dwarf: &gimli::Dwarf<DwarfReader>,
unit: &gimli::Unit<DwarfReader>,
tag: &gimli::DebuggingInformationEntry<DwarfReader>,
print_depth: usize,
) {
let mut attrs = tag.attrs();
while let Some(attr) = attrs.next().unwrap() {
for _ in 0..print_depth {
print!("\t");
}
print!("{}: ", attr.name());
match attr.value() {
AttributeValue::Addr(a) => println!("{a:#010x}"),
AttributeValue::DebugStrRef(str_ref) => {
let val = dwarf.string(str_ref).unwrap();
println!("{}", std::str::from_utf8(&val).unwrap());
}
AttributeValue::Exprloc(e) => {
let mut evaluation = e.evaluation(unit.encoding());
let mut result = evaluation.evaluate().unwrap();
while let Some(next) = iterate(result, core, &mut evaluation, stackframe_cfa) {
result = next;
}
let result = evaluation.result();
println!("Expression: {:x?}", &result[0]);
}
AttributeValue::LocationListsRef(_) => println!("LocationList"),
AttributeValue::DebugLocListsBase(_) => println!(" LocationList"),
AttributeValue::DebugLocListsIndex(_) => println!(" LocationList"),
_ => println!("print_all_attributes {:?}", attr.value()),
}
}
}
#[allow(dead_code)]
fn iterate(
result: EvaluationResult<DwarfReader>,
core: &mut Core,
evaluation: &mut gimli::Evaluation<DwarfReader>,
stackframe_cfa: Option<u64>,
) -> Option<EvaluationResult<DwarfReader>> {
let resume_result = match result {
EvaluationResult::Complete => return None,
EvaluationResult::RequiresMemory { address, size, .. } => {
let mut buff = vec![0u8; size as usize];
core.read(address, &mut buff)
.expect("Failed to read memory");
let value = match size {
1 => gimli::Value::U8(buff[0]),
2 => gimli::Value::U16(u16::from_be_bytes([buff[0], buff[1]])),
4 => gimli::Value::U32(u32::from_be_bytes([buff[0], buff[1], buff[2], buff[3]])),
x => unimplemented!("Requested memory with size {x}, which is not supported yet."),
};
evaluation.resume_with_memory(value)
}
EvaluationResult::RequiresFrameBase => {
evaluation.resume_with_frame_base(stackframe_cfa.unwrap())
}
EvaluationResult::RequiresRegister {
register,
base_type,
} => {
let raw_value = core
.read_core_reg::<u64>(register.0)
.expect("Failed to read memory");
if base_type != gimli::UnitOffset(0) {
unimplemented!(
"Support for units in RequiresRegister request is not yet implemented."
)
}
evaluation.resume_with_register(gimli::Value::Generic(raw_value))
}
EvaluationResult::RequiresRelocatedAddress(address_index) => {
evaluation.resume_with_relocated_address(address_index)
}
x => {
println!("print_all_attributes {x:?}");
todo!()
}
};
Some(resume_result.unwrap())
}