Skip to content

Commit d969819

Browse files
authored
Guess endianness of "erased" DWARF info (#104)
1 parent f4a67ee commit d969819

File tree

3 files changed

+47
-27
lines changed

3 files changed

+47
-27
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ Dumps DWARF 1.1 information from an ELF file. (Does **not** support DWARF 2+)
297297

298298
```shell
299299
$ dtk dwarf dump input.elf
300+
# or, to include data that was stripped by MWLD
301+
$ dtk dwarf dump input.elf --include-erased
300302
```
301303

302304
### elf disasm

src/util/dwarf.rs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ pub struct Tag {
358358
pub kind: TagKind,
359359
pub is_erased: bool, // Tag was deleted but has been reconstructed
360360
pub is_erased_root: bool, // Tag is erased and is the root of a tree of erased tags
361+
pub data_endian: Endian, // Endianness of the tag data (could be different from the address endianness for erased tags)
361362
pub attributes: Vec<Attribute>,
362363
}
363364

@@ -554,6 +555,7 @@ where
554555
kind: TagKind::Padding,
555556
is_erased,
556557
is_erased_root: false,
558+
data_endian,
557559
attributes: Vec::new(),
558560
});
559561
return Ok(tags);
@@ -563,26 +565,42 @@ where
563565
let tag = TagKind::try_from(tag_num).context("Unknown DWARF tag type")?;
564566
if tag == TagKind::Padding {
565567
if include_erased {
566-
// Erased entries that have become padding are little-endian, and we
567-
// have to guess the length and tag of the first entry. We assume
568-
// the entry is either a variable or a function, and read until we
569-
// find the high_pc attribute. Only MwGlobalRef will follow, and
570-
// these are unlikely to be confused with the length of the next
571-
// entry.
568+
// Erased entries that have become padding could be either
569+
// little-endian or big-endian, and we have to guess the length and
570+
// tag of the first entry. We assume the entry is either a variable
571+
// or a function, and read until we find the high_pc attribute. Only
572+
// MwGlobalRef will follow, and these are unlikely to be confused
573+
// with the length of the next entry.
572574
let mut attributes = Vec::new();
573575
let mut is_function = false;
576+
577+
// Guess endianness based on first attribute
578+
let data_endian = if is_erased {
579+
data_endian
580+
} else {
581+
// Peek next two bytes
582+
let mut buf = [0u8; 2];
583+
reader.read_exact(&mut buf)?;
584+
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), data_endian)?;
585+
reader.seek(SeekFrom::Current(-2))?;
586+
match AttributeKind::try_from(attr_tag) {
587+
Ok(_) => data_endian,
588+
Err(_) => data_endian.flip(),
589+
}
590+
};
591+
574592
while reader.stream_position()? < position + size as u64 {
575593
// Peek next two bytes
576594
let mut buf = [0u8; 2];
577595
reader.read_exact(&mut buf)?;
578-
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), Endian::Little)?;
596+
let attr_tag = u16::from_reader(&mut Cursor::new(&buf), data_endian)?;
579597
reader.seek(SeekFrom::Current(-2))?;
580598

581599
if is_function && attr_tag != AttributeKind::MwGlobalRef as u16 {
582600
break;
583601
}
584602

585-
let attr = read_attribute(reader, Endian::Little, addr_endian)?;
603+
let attr = read_attribute(reader, data_endian, addr_endian)?;
586604
if attr.kind == AttributeKind::HighPc {
587605
is_function = true;
588606
}
@@ -594,12 +612,13 @@ where
594612
kind,
595613
is_erased: true,
596614
is_erased_root: true,
615+
data_endian,
597616
attributes,
598617
});
599618

600619
// Read the rest of the tags
601620
while reader.stream_position()? < position + size as u64 {
602-
for tag in read_tags(reader, Endian::Little, addr_endian, include_erased, true)? {
621+
for tag in read_tags(reader, data_endian, addr_endian, include_erased, true)? {
603622
tags.push(tag);
604623
}
605624
}
@@ -616,6 +635,7 @@ where
616635
kind: tag,
617636
is_erased,
618637
is_erased_root: false,
638+
data_endian,
619639
attributes,
620640
});
621641
}
@@ -2028,9 +2048,9 @@ fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result<ArrayType> {
20282048
(AttributeKind::Sibling, _) => {}
20292049
(AttributeKind::SubscrData, AttributeValue::Block(data)) => {
20302050
subscr_data =
2031-
Some(process_array_subscript_data(data, info.e, tag.is_erased).with_context(
2032-
|| format!("Failed to process SubscrData for tag: {tag:?}"),
2033-
)?)
2051+
Some(process_array_subscript_data(data, info.e).with_context(|| {
2052+
format!("Failed to process SubscrData for tag: {tag:?}")
2053+
})?)
20342054
}
20352055
(AttributeKind::Ordering, val) => match val {
20362056
AttributeValue::Data2(d2) => {
@@ -2056,11 +2076,7 @@ fn process_array_tag(info: &DwarfInfo, tag: &Tag) -> Result<ArrayType> {
20562076
Ok(ArrayType { element_type: Box::from(element_type), dimensions })
20572077
}
20582078

2059-
fn process_array_subscript_data(
2060-
data: &[u8],
2061-
e: Endian,
2062-
is_erased: bool,
2063-
) -> Result<(Type, Vec<ArrayDimension>)> {
2079+
fn process_array_subscript_data(data: &[u8], e: Endian) -> Result<(Type, Vec<ArrayDimension>)> {
20642080
let mut element_type = None;
20652081
let mut dimensions = Vec::new();
20662082
let mut data = data;
@@ -2101,8 +2117,7 @@ fn process_array_subscript_data(
21012117
SubscriptFormat::ElementType => {
21022118
let mut cursor = Cursor::new(data);
21032119
// TODO: is this the right endianness to use for erased tags?
2104-
let type_attr =
2105-
read_attribute(&mut cursor, if is_erased { Endian::Little } else { e }, e)?;
2120+
let type_attr = read_attribute(&mut cursor, e, e)?;
21062121
element_type = Some(process_type(&type_attr, e)?);
21072122
data = &data[cursor.position() as usize..];
21082123
}
@@ -2456,10 +2471,7 @@ fn process_subroutine_parameter_tag(info: &DwarfInfo, tag: &Tag) -> Result<Subro
24562471
) => kind = Some(process_type(attr, info.e)?),
24572472
(AttributeKind::Location, AttributeValue::Block(block)) => {
24582473
if !block.is_empty() {
2459-
location = Some(process_variable_location(
2460-
block,
2461-
if tag.is_erased { Endian::Little } else { info.e },
2462-
)?);
2474+
location = Some(process_variable_location(block, tag.data_endian)?);
24632475
}
24642476
}
24652477
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {
@@ -2514,10 +2526,7 @@ fn process_local_variable_tag(info: &DwarfInfo, tag: &Tag) -> Result<SubroutineV
25142526
) => kind = Some(process_type(attr, info.e)?),
25152527
(AttributeKind::Location, AttributeValue::Block(block)) => {
25162528
if !block.is_empty() {
2517-
location = Some(process_variable_location(
2518-
block,
2519-
if tag.is_erased { Endian::Little } else { info.e },
2520-
)?);
2529+
location = Some(process_variable_location(block, tag.data_endian)?);
25212530
}
25222531
}
25232532
(AttributeKind::MwDwarf2Location, AttributeValue::Block(_block)) => {

src/util/reader.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ impl From<object::Endianness> for Endian {
2020
}
2121
}
2222

23+
impl Endian {
24+
pub fn flip(self) -> Self {
25+
match self {
26+
Endian::Big => Endian::Little,
27+
Endian::Little => Endian::Big,
28+
}
29+
}
30+
}
31+
2332
pub const DYNAMIC_SIZE: usize = 0;
2433

2534
pub const fn struct_size<const N: usize>(fields: [usize; N]) -> usize {

0 commit comments

Comments
 (0)