use crate::ctab::Reader;
use crate::{Result, Type, error};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SourceLine {
pub line: u16,
pub file: Option<u16>,
pub offset: u32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Location {
pub instruction: u32,
pub components: [Option<u16>; 4],
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Variable {
pub scope: Option<String>,
pub name: String,
pub ty: Type,
pub locations: Vec<Location>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DebugInfo {
pub path: String,
pub creator: String,
pub reserved: u32,
pub files: Vec<String>,
pub lines: Vec<SourceLine>,
pub variables: Vec<Variable>,
pub entry: String,
}
pub(crate) fn read(data: &[u8], word: usize) -> Result<DebugInfo> {
let r = Reader {
data,
word,
tag: "DBUG",
};
let header = |n: usize| r.offset(n.saturating_mul(4));
let table = |count: usize, size: usize| -> Result<usize> {
let at = header(count.saturating_add(1))?;
r.range(at, r.bytes(at, header(count)?, size)?)?;
Ok(at)
};
let (file_count, files_at) = (header(3)?, table(3, 4)?);
let mut files = Vec::new();
for n in 0..file_count {
files.push(r.string(r.offset(r.plus(files_at, r.bytes(files_at, n, 4)?)?)?)?);
}
let (line_count, lines_at) = (header(5)?, table(5, 8)?);
let mut lines = Vec::new();
for n in 0..line_count {
let at = r.plus(lines_at, r.bytes(lines_at, n, 8)?)?;
let file = r.u16(r.plus(at, 2)?)?;
if file != 0xffff && usize::from(file) >= file_count {
return Err(error(
r.word_of(at),
format!("DBUG line record {n} names file {file}, where there are {file_count}"),
));
}
lines.push(SourceLine {
line: r.u16(at)?,
file: (file != 0xffff).then_some(file),
offset: r.u32(r.plus(at, 4)?)?,
});
}
let (variable_count, variables_at) = (header(7)?, table(7, 20)?);
let mut variables = Vec::new();
let mut budget = 4096;
for n in 0..variable_count {
let at = r.plus(variables_at, r.bytes(variables_at, n, 20)?)?;
let scope = r.offset(at)?;
let location_count = r.offset(r.plus(at, 12)?)?;
let locations_at = r.offset(r.plus(at, 16)?)?;
r.range(locations_at, r.bytes(locations_at, location_count, 12)?)?;
let mut locations = Vec::new();
for l in 0..location_count {
let record = r.plus(locations_at, r.bytes(locations_at, l, 12)?)?;
let instruction = r.u32(record)?;
if usize::try_from(instruction).map_or(true, |i| i >= line_count) {
return Err(error(
r.word_of(record),
format!(
"DBUG location of variable {n} names instruction {instruction}, where there are {line_count}"
),
));
}
let mut components = [None; 4];
for (lane, slot) in components.iter_mut().enumerate() {
let c = r.u16(r.plus(record, lane.saturating_mul(2).saturating_add(4))?)?;
*slot = (c != 0xffff).then_some(c);
}
locations.push(Location {
instruction,
components,
});
}
variables.push(Variable {
scope: if scope == 0 { None } else { Some(r.string(scope)?) },
name: r.string(r.offset(r.plus(at, 4)?)?)?,
ty: r.ty(r.offset(r.plus(at, 8)?)?, 0, &mut budget)?,
locations,
});
}
Ok(DebugInfo {
path: r.string(header(0)?)?,
creator: r.string(header(1)?)?,
reserved: r.u32(8)?,
files,
lines,
variables,
entry: r.string(header(9)?)?,
})
}