Guess endianness of "erased" DWARF info (#104)

This commit is contained in:
cadmic 2025-06-09 21:44:39 -07:00 committed by GitHub
parent f4a67ee619
commit d969819b78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 47 additions and 27 deletions

View File

@ -297,6 +297,8 @@ Dumps DWARF 1.1 information from an ELF file. (Does **not** support DWARF 2+)
```shell ```shell
$ dtk dwarf dump input.elf $ dtk dwarf dump input.elf
# or, to include data that was stripped by MWLD
$ dtk dwarf dump input.elf --include-erased
``` ```
### elf disasm ### elf disasm

View File

@ -358,6 +358,7 @@ pub struct Tag {
pub kind: TagKind, pub kind: TagKind,
pub is_erased: bool, // Tag was deleted but has been reconstructed pub is_erased: bool, // Tag was deleted but has been reconstructed
pub is_erased_root: bool, // Tag is erased and is the root of a tree of erased tags pub is_erased_root: bool, // Tag is erased and is the root of a tree of erased tags
pub data_endian: Endian, // Endianness of the tag data (could be different from the address endianness for erased tags)
pub attributes: Vec<Attribute>, pub attributes: Vec<Attribute>,
} }
@ -554,6 +555,7 @@ where
kind: TagKind::Padding, kind: TagKind::Padding,
is_erased, is_erased,
is_erased_root: false, is_erased_root: false,
data_endian,
attributes: Vec::new(), attributes: Vec::new(),
}); });
return Ok(tags); return Ok(tags);
@ -563,26 +565,42 @@ where
let tag = TagKind::try_from(tag_num).context("Unknown DWARF tag type")?; let tag = TagKind::try_from(tag_num).context("Unknown DWARF tag type")?;
if tag == TagKind::Padding { if tag == TagKind::Padding {
if include_erased { if include_erased {
// Erased entries that have become padding are little-endian, and we // Erased entries that have become padding could be either
// have to guess the length and tag of the first entry. We assume // little-endian or big-endian, and we have to guess the length and
// the entry is either a variable or a function, and read until we // tag of the first entry. We assume the entry is either a variable
// find the high_pc attribute. Only MwGlobalRef will follow, and // or a function, and read until we find the high_pc attribute. Only
// these are unlikely to be confused with the length of the next // MwGlobalRef will follow, and these are unlikely to be confused
// entry. // with the length of the next entry.
let mut attributes = Vec::new(); let mut attributes = Vec::new();
let mut is_function = false; let mut is_function = false;
// Guess endianness based on first attribute
let data_endian = if is_erased {
data_endian
} else {
// Peek next two bytes
let mut buf = [0u8; 2];
reader.read_exact(&mut buf)?;
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), data_endian)?;
reader.seek(SeekFrom::Current(-2))?;
match AttributeKind::try_from(attr_tag) {
Ok(_) => data_endian,
Err(_) => data_endian.flip(),
}
};
while reader.stream_position()? < position + size as u64 { while reader.stream_position()? < position + size as u64 {
// Peek next two bytes // Peek next two bytes
let mut buf = [0u8; 2]; let mut buf = [0u8; 2];
reader.read_exact(&mut buf)?; reader.read_exact(&mut buf)?;
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), Endian::Little)?; let attr_tag = u16::from_reader(&mut Cursor::new(&buf), data_endian)?;
reader.seek(SeekFrom::Current(-2))?; reader.seek(SeekFrom::Current(-2))?;
if is_function && attr_tag != AttributeKind::MwGlobalRef as u16 { if is_function && attr_tag != AttributeKind::MwGlobalRef as u16 {
break; break;
} }
let attr = read_attribute(reader, Endian::Little, addr_endian)?; let attr = read_attribute(reader, data_endian, addr_endian)?;
if attr.kind == AttributeKind::HighPc { if attr.kind == AttributeKind::HighPc {
is_function = true; is_function = true;
} }
@ -594,12 +612,13 @@ where
kind, kind,
is_erased: true, is_erased: true,
is_erased_root: true, is_erased_root: true,
data_endian,
attributes, attributes,
}); });
// Read the rest of the tags // Read the rest of the tags
while reader.stream_position()? < position + size as u64 { while reader.stream_position()? < position + size as u64 {
for tag in read_tags(reader, Endian::Little, addr_endian, include_erased, true)? { for tag in read_tags(reader, data_endian, addr_endian, include_erased, true)? {
tags.push(tag); tags.push(tag);
} }
} }
@ -616,6 +635,7 @@ where
kind: tag, kind: tag,
is_erased, is_erased,
is_erased_root: false, is_erased_root: false,
data_endian,
attributes, attributes,
}); });
} }
@ -2028,9 +2048,9 @@ fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result<ArrayType> {
(AttributeKind::Sibling, _) => {} (AttributeKind::Sibling, _) => {}
(AttributeKind::SubscrData, AttributeValue::Block(data)) => { (AttributeKind::SubscrData, AttributeValue::Block(data)) => {
subscr_data = subscr_data =
Some(process_array_subscript_data(data, info.e, tag.is_erased).with_context( Some(process_array_subscript_data(data, info.e).with_context(|| {
|| format!("Failed to process SubscrData for tag: {tag:?}"), format!("Failed to process SubscrData for tag: {tag:?}")
)?) })?)
} }
(AttributeKind::Ordering, val) => match val { (AttributeKind::Ordering, val) => match val {
AttributeValue::Data2(d2) => { AttributeValue::Data2(d2) => {
@ -2056,11 +2076,7 @@ fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result<ArrayType> {
Ok(ArrayType { element_type: Box::from(element_type), dimensions }) Ok(ArrayType { element_type: Box::from(element_type), dimensions })
} }
fn process_array_subscript_data( fn process_array_subscript_data(data: &[u8], e: Endian) -> Result<(Type, Vec<ArrayDimension>)> {
data: &[u8],
e: Endian,
is_erased: bool,
) -> Result<(Type, Vec<ArrayDimension>)> {
let mut element_type = None; let mut element_type = None;
let mut dimensions = Vec::new(); let mut dimensions = Vec::new();
let mut data = data; let mut data = data;
@ -2101,8 +2117,7 @@ fn process_array_subscript_data(
SubscriptFormat::ElementType => { SubscriptFormat::ElementType => {
let mut cursor = Cursor::new(data); let mut cursor = Cursor::new(data);
// TODO: is this the right endianness to use for erased tags? // TODO: is this the right endianness to use for erased tags?
let type_attr = let type_attr = read_attribute(&mut cursor, e, e)?;
read_attribute(&mut cursor, if is_erased { Endian::Little } else { e }, e)?;
element_type = Some(process_type(&type_attr, e)?); element_type = Some(process_type(&type_attr, e)?);
data = &data[cursor.position() as usize..]; data = &data[cursor.position() as usize..];
} }
@ -2456,10 +2471,7 @@ fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subro
) => kind = Some(process_type(attr, info.e)?), ) => kind = Some(process_type(attr, info.e)?),
(AttributeKind::Location, AttributeValue::Block(block)) => { (AttributeKind::Location, AttributeValue::Block(block)) => {
if !block.is_empty() { if !block.is_empty() {
location = Some(process_variable_location( location = Some(process_variable_location(block, tag.data_endian)?);
block,
if tag.is_erased { Endian::Little } else { info.e },
)?);
} }
} }
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => { (AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
@ -2514,10 +2526,7 @@ fn process_local_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineV
) => kind = Some(process_type(attr, info.e)?), ) => kind = Some(process_type(attr, info.e)?),
(AttributeKind::Location, AttributeValue::Block(block)) => { (AttributeKind::Location, AttributeValue::Block(block)) => {
if !block.is_empty() { if !block.is_empty() {
location = Some(process_variable_location( location = Some(process_variable_location(block, tag.data_endian)?);
block,
if tag.is_erased { Endian::Little } else { info.e },
)?);
} }
} }
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => { (AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {

View File

@ -20,6 +20,15 @@ impl From<object::Endianness> for Endian {
} }
} }
impl Endian {
pub fn flip(self) -> Self {
match self {
Endian::Big => Endian::Little,
Endian::Little => Endian::Big,
}
}
}
pub const DYNAMIC_SIZE: usize = 0; pub const DYNAMIC_SIZE: usize = 0;
pub const fn struct_size<const N: usize>(fields: [usize; N]) -> usize { pub const fn struct_size<const N: usize>(fields: [usize; N]) -> usize {