layout_audit/dwarf/
mod.rs

1mod context;
2mod expr;
3mod types;
4
5pub use context::{DwarfContext, is_go_internal_type};
6pub use types::TypeResolver;
7
8use crate::loader::DwarfSlice;
9use gimli::{AttributeValue, DebugInfoOffset, UnitHeader, UnitOffset};
10
11/// Extract a u64 value from a DWARF attribute, handling various encoding forms.
12/// Returns None for negative Sdata values (invalid for offsets/sizes/indices).
13/// Used by both context.rs and types.rs to avoid duplication.
14pub(crate) fn read_u64_from_attr(attr: Option<AttributeValue<DwarfSlice<'_>>>) -> Option<u64> {
15    match attr? {
16        AttributeValue::FileIndex(idx) => Some(idx),
17        AttributeValue::Udata(v) => Some(v),
18        AttributeValue::Data1(v) => Some(v as u64),
19        AttributeValue::Data2(v) => Some(v as u64),
20        AttributeValue::Data4(v) => Some(v as u64),
21        AttributeValue::Data8(v) => Some(v),
22        // Negative Sdata values are invalid for offsets/sizes/indices - return None.
23        AttributeValue::Sdata(v) if v >= 0 => Some(v as u64),
24        _ => None,
25    }
26}
27
28/// Convert a DebugInfoRef (section offset) to a UnitOffset (unit-relative offset).
29/// Returns None if the reference is invalid (cross-unit or corrupted DWARF).
30/// Used by both types.rs and context.rs for consistent cross-unit reference handling.
31pub(crate) fn debug_info_ref_to_unit_offset<R: gimli::Reader>(
32    debug_info_offset: DebugInfoOffset<R::Offset>,
33    unit_header: &UnitHeader<R>,
34) -> Option<UnitOffset<R::Offset>>
35where
36    R::Offset: std::ops::Sub<Output = R::Offset>,
37{
38    let unit_debug_offset = unit_header.offset().as_debug_info_offset()?;
39    // Use checked arithmetic to detect invalid cross-unit references.
40    // If debug_info_offset < unit_debug_offset, this is corrupted DWARF.
41    if debug_info_offset.0 >= unit_debug_offset.0 {
42        Some(UnitOffset(debug_info_offset.0 - unit_debug_offset.0))
43    } else {
44        None
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use gimli::{
52        DebugAbbrevOffset, DebugInfoOffset, Encoding, Format, UnitHeader, UnitSectionOffset,
53        UnitType,
54    };
55
56    #[test]
57    fn read_u64_from_attr_handles_encodings() {
58        assert_eq!(read_u64_from_attr(Some(AttributeValue::Data1(1))), Some(1));
59        assert_eq!(read_u64_from_attr(Some(AttributeValue::Data2(2))), Some(2));
60        assert_eq!(read_u64_from_attr(Some(AttributeValue::Data4(3))), Some(3));
61        assert_eq!(read_u64_from_attr(Some(AttributeValue::Data8(4))), Some(4));
62        assert_eq!(read_u64_from_attr(Some(AttributeValue::Udata(5))), Some(5));
63        assert_eq!(read_u64_from_attr(Some(AttributeValue::Sdata(-1))), None);
64        assert_eq!(read_u64_from_attr(Some(AttributeValue::Sdata(7))), Some(7));
65        assert_eq!(read_u64_from_attr(Some(AttributeValue::FileIndex(9))), Some(9));
66    }
67
68    #[test]
69    fn debug_info_ref_to_unit_offset_validates_unit() {
70        let encoding = Encoding { address_size: 8, format: Format::Dwarf32, version: 4 };
71        let unit_offset = UnitSectionOffset::DebugInfoOffset(DebugInfoOffset(0x100));
72        let header = UnitHeader::new(
73            encoding,
74            0,
75            UnitType::Compilation,
76            DebugAbbrevOffset(0),
77            unit_offset,
78            DwarfSlice::new(&[], gimli::RunTimeEndian::Little),
79        );
80
81        let within = debug_info_ref_to_unit_offset(DebugInfoOffset(0x100), &header);
82        assert_eq!(within, Some(UnitOffset(0)));
83
84        let outside = debug_info_ref_to_unit_offset(DebugInfoOffset(0x0ff), &header);
85        assert_eq!(outside, None);
86    }
87}