From 165ff72e79186756c686fbcf38d25f037090c901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannah=20Pascal-No=C3=ABl?= Date: Wed, 4 Nov 2026 16:52:00 +0000 Subject: [PATCH] Support unaligned C13 debug subsection lengths The length does not include the padding that follows it up to the next multiple of four bytes. PDBs from link.exe never hit this because their subsection lengths already are multiples of four. But the .NET Native toolchain (ILC) writes unpadded lengths for its FuncMDTokenMap and TypeMDTokenMap subsections. Co-Authored-By: Claude Sonnet 5.5 --- src/modi/c13.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/modi/c13.rs b/src/modi/c13.rs index 223bb99..b7187a4 100644 --- a/src/modi/c13.rs +++ b/src/modi/c13.rs @@ -105,6 +105,11 @@ impl<'a> FallibleIterator for DebugSubsectionIterator<'a> { while !self.buf.is_empty() { let header = self.buf.parse::()?; let data = self.buf.take(header.len())?; + + // Padding is not included in the length of the subsection, skip. + let padding = (4 - header.len() % 4) % 4; + self.buf.take(padding.min(self.buf.len()))?; + let kind = match header.kind()? { Some(kind) => kind, None => continue, @@ -1421,6 +1426,54 @@ mod tests { use crate::symbol::BinaryAnnotations; + fn subsection(kind: u32, data: &[u8], padded: bool) -> Vec { + let mut bytes = Vec::new(); + bytes.extend_from_slice(&kind.to_le_bytes()); + bytes.extend_from_slice(&(data.len() as u32).to_le_bytes()); + bytes.extend_from_slice(data); + if padded { + while bytes.len() % 4 != 0 { + bytes.push(0); + } + } + bytes + } + + #[test] + fn test_subsection_iterator_skips_padding() { + // The length of a subsection does not include its padding. + let mut data = subsection(0xfa, &[1, 2, 3, 4, 5], true); + data.extend(subsection(0xfb, &[6, 7, 8, 9, 10, 11], true)); + data.extend(subsection(0xf4, &[12, 13, 14, 15], true)); + + let mut iter = DebugSubsectionIterator::new(&data); + + let first = iter.next().unwrap().unwrap(); + assert_eq!(first.kind, DebugSubsectionKind::FuncMDTokenMap); + assert_eq!(first.data, &[1, 2, 3, 4, 5]); + + let second = iter.next().unwrap().unwrap(); + assert_eq!(second.kind, DebugSubsectionKind::TypeMDTokenMap); + assert_eq!(second.data, &[6, 7, 8, 9, 10, 11]); + + let third = iter.next().unwrap().unwrap(); + assert_eq!(third.kind, DebugSubsectionKind::FileChecksums); + assert_eq!(third.data, &[12, 13, 14, 15]); + + assert!(iter.next().unwrap().is_none()); + } + + #[test] + fn test_subsection_iterator_last_subsection_without_padding() { + let mut data = subsection(0xfa, &[1, 2, 3], true); + data.extend(subsection(0xfb, &[4, 5, 6], false)); + + let mut iter = DebugSubsectionIterator::new(&data); + assert_eq!(iter.next().unwrap().unwrap().data, &[1, 2, 3]); + assert_eq!(iter.next().unwrap().unwrap().data, &[4, 5, 6]); + assert!(iter.next().unwrap().is_none()); + } + #[test] fn test_line_number_header() { assert_eq!(mem::size_of::(), 8);