1 // Copyright 2017 pdb Developers 2 // 3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5 // http://opensource.org/licenses/MIT>, at your option. This file may not be 6 // copied, modified, or distributed except according to those terms. 7 8 // DBI = "Debug Information" 9 10 use std::borrow::Cow; 11 use std::fmt; 12 use std::result; 13 14 use crate::common::*; 15 use crate::msf::*; 16 use crate::FallibleIterator; 17 18 /// Provides access to the "DBI" stream inside the PDB. 19 /// 20 /// This is only minimally implemented; it's really just so `PDB` can find the global symbol table. 21 /// 22 /// # Example 23 /// 24 /// ``` 25 /// # use pdb::FallibleIterator; 26 /// # 27 /// # fn test() -> pdb::Result<usize> { 28 /// let file = std::fs::File::open("fixtures/self/foo.pdb")?; 29 /// let mut pdb = pdb::PDB::open(file)?; 30 /// 31 /// let dbi = pdb.debug_information()?; 32 33 /// 34 /// # let mut count: usize = 0; 35 /// let mut modules = dbi.modules()?; 36 /// while let Some(module) = modules.next()? { 37 /// println!("module name: {}, object file name: {}", 38 /// module.module_name(), module.object_file_name()); 39 /// # count += 1; 40 /// } 41 /// 42 /// # Ok(count) 43 /// # } 44 /// # assert!(test().expect("test") == 194); 45 #[derive(Debug)] 46 pub struct DebugInformation<'s> { 47 stream: Stream<'s>, 48 header: DBIHeader, 49 header_len: usize, 50 } 51 52 impl<'s> DebugInformation<'s> { parse(stream: Stream<'s>) -> Result<Self>53 pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> { 54 let mut buf = stream.parse_buffer(); 55 let header = DBIHeader::parse_buf(&mut buf)?; 56 let header_len = buf.pos(); 57 58 Ok(DebugInformation { 59 stream, 60 header, 61 header_len, 62 }) 63 } 64 header(&self) -> DBIHeader65 pub(crate) fn header(&self) -> DBIHeader { 66 self.header 67 } 68 69 /// Returns the target's machine type (architecture). machine_type(&self) -> Result<MachineType>70 pub fn machine_type(&self) -> Result<MachineType> { 71 Ok(self.header.machine_type.into()) 72 } 73 74 /// Returns this PDB's original `age`. 75 /// 76 /// This number is written by the linker and should be equal to the image's `age` value. In 77 /// contrast, [`PDBInformation::age`] may be bumped by other tools and should be greater or 78 /// equal to the image's `age` value. 79 /// 80 /// Old PDB files may not specify an age, in which case only [`PDBInformation::age`] should be 81 /// checked for matching the image. 82 /// 83 /// [`PDBInformation::age`]: crate::PDBInformation::age age(&self) -> Option<u32>84 pub fn age(&self) -> Option<u32> { 85 match self.header.age { 86 0 => None, 87 age => Some(age), 88 } 89 } 90 91 /// Returns an iterator that can traverse the modules list in sequential order. modules(&self) -> Result<ModuleIter<'_>>92 pub fn modules(&self) -> Result<ModuleIter<'_>> { 93 let mut buf = self.stream.parse_buffer(); 94 // drop the header 95 buf.take(self.header_len)?; 96 let modules_buf = buf.take(self.header.module_list_size as usize)?; 97 Ok(ModuleIter { 98 buf: modules_buf.into(), 99 }) 100 } 101 102 /// Returns an iterator that can traverse the section contributions list in sequential order. section_contributions(&self) -> Result<DBISectionContributionIter<'_>>103 pub fn section_contributions(&self) -> Result<DBISectionContributionIter<'_>> { 104 let mut buf = self.stream.parse_buffer(); 105 // drop the header and modules list 106 buf.take(self.header_len + self.header.module_list_size as usize)?; 107 let contributions_buf = buf.take(self.header.section_contribution_size as usize)?; 108 DBISectionContributionIter::parse(contributions_buf.into()) 109 } 110 } 111 112 /// The version of the PDB format. 113 /// 114 /// This version type is used in multiple locations: the DBI header, and the PDBI header. 115 #[non_exhaustive] 116 #[derive(Debug, Copy, Clone)] 117 #[allow(missing_docs)] 118 pub enum HeaderVersion { 119 V41, 120 V50, 121 V60, 122 V70, 123 V110, 124 OtherValue(u32), 125 } 126 127 impl From<u32> for HeaderVersion { 128 #[allow(clippy::inconsistent_digit_grouping)] from(v: u32) -> Self129 fn from(v: u32) -> Self { 130 match v { 131 93_08_03 => HeaderVersion::V41, 132 1996_03_07 => HeaderVersion::V50, 133 1997_06_06 => HeaderVersion::V60, 134 1999_09_03 => HeaderVersion::V70, 135 2009_12_01 => HeaderVersion::V110, 136 _ => HeaderVersion::OtherValue(v), 137 } 138 } 139 } 140 141 /// A DBI header -- `NewDBIHdr`, really -- parsed from a stream. 142 /// Reference: 143 /// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L124 144 #[derive(Debug, Copy, Clone)] 145 pub(crate) struct DBIHeader { 146 pub signature: u32, 147 pub version: HeaderVersion, 148 pub age: u32, 149 pub gs_symbols_stream: StreamIndex, 150 151 /* 152 https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L143-L155: 153 union { 154 struct { 155 USHORT usVerPdbDllMin : 8; // minor version and 156 USHORT usVerPdbDllMaj : 7; // major version and 157 USHORT fNewVerFmt : 1; // flag telling us we have rbld stored elsewhere (high bit of original major version) 158 } vernew; // that built this pdb last. 159 struct { 160 USHORT usVerPdbDllRbld: 4; 161 USHORT usVerPdbDllMin : 7; 162 USHORT usVerPdbDllMaj : 5; 163 } verold; 164 USHORT usVerAll; 165 }; 166 */ 167 pub internal_version: u16, 168 pub ps_symbols_stream: StreamIndex, 169 // "build version of the pdb dll that built this pdb last." 170 pub pdb_dll_build_version: u16, 171 172 pub symbol_records_stream: StreamIndex, 173 174 // "rbld version of the pdb dll that built this pdb last." 175 pub pdb_dll_rbld_version: u16, 176 pub module_list_size: u32, 177 pub section_contribution_size: u32, 178 pub section_map_size: u32, 179 pub file_info_size: u32, 180 181 // "size of the Type Server Map substream" 182 pub type_server_map_size: u32, 183 184 // "index of MFC type server" 185 pub mfc_type_server_index: u32, 186 187 // "size of optional DbgHdr info appended to the end of the stream" 188 pub debug_header_size: u32, 189 190 // "number of bytes in EC substream, or 0 if EC no EC enabled Mods" 191 pub ec_substream_size: u32, 192 193 /* 194 https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L187-L192: 195 USHORT fIncLink:1; // true if linked incrmentally (really just if ilink thunks are present) 196 USHORT fStripped:1; // true if PDB::CopyTo stripped the private data out 197 USHORT fCTypes:1; // true if this PDB is using CTypes. 198 USHORT unused:13; // reserved, must be 0. 199 */ 200 pub flags: u16, 201 202 pub machine_type: u16, 203 pub reserved: u32, 204 } 205 206 impl DBIHeader { parse(stream: Stream<'_>) -> Result<Self>207 pub fn parse(stream: Stream<'_>) -> Result<Self> { 208 Self::parse_buf(&mut stream.parse_buffer()) 209 } 210 parse_buf(buf: &mut ParseBuffer<'_>) -> Result<Self>211 fn parse_buf(buf: &mut ParseBuffer<'_>) -> Result<Self> { 212 let header = DBIHeader { 213 signature: buf.parse_u32()?, 214 version: From::from(buf.parse_u32()?), 215 age: buf.parse_u32()?, 216 gs_symbols_stream: buf.parse()?, 217 internal_version: buf.parse_u16()?, 218 ps_symbols_stream: buf.parse()?, 219 pdb_dll_build_version: buf.parse_u16()?, 220 symbol_records_stream: buf.parse()?, 221 pdb_dll_rbld_version: buf.parse_u16()?, 222 module_list_size: buf.parse_u32()?, 223 section_contribution_size: buf.parse_u32()?, 224 section_map_size: buf.parse_u32()?, 225 file_info_size: buf.parse_u32()?, 226 type_server_map_size: buf.parse_u32()?, 227 mfc_type_server_index: buf.parse_u32()?, 228 debug_header_size: buf.parse_u32()?, 229 ec_substream_size: buf.parse_u32()?, 230 flags: buf.parse_u16()?, 231 machine_type: buf.parse_u16()?, 232 reserved: buf.parse_u32()?, 233 }; 234 235 if header.signature != u32::max_value() { 236 // this is likely a DBIHdr, not a NewDBIHdr 237 // it could be promoted: 238 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.cpp#L291-L313 239 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/langapi/include/pdb.h#L1180-L1184 240 // but that seems like a lot of work 241 return Err(Error::UnimplementedFeature("ancient DBI header")); 242 } 243 244 Ok(header) 245 } 246 } 247 248 /// The target machine's architecture. 249 /// Reference: <https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#machine-types> 250 #[non_exhaustive] 251 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 252 pub enum MachineType { 253 /// The contents of this field are assumed to be applicable to any machine type. 254 Unknown = 0x0, 255 /// Matsushita AM33 256 Am33 = 0x13, 257 /// x64 258 Amd64 = 0x8664, 259 /// ARM little endian 260 Arm = 0x1C0, 261 /// ARM64 little endian 262 Arm64 = 0xAA64, 263 /// ARM Thumb-2 little endian 264 ArmNT = 0x1C4, 265 /// EFI byte code 266 Ebc = 0xEBC, 267 /// Intel 386 or later processors and compatible processors 268 X86 = 0x14C, 269 /// Intel Itanium processor family 270 Ia64 = 0x200, 271 /// Mitsubishi M32R little endian 272 M32R = 0x9041, 273 /// MIPS16 274 Mips16 = 0x266, 275 /// MIPS with FPU 276 MipsFpu = 0x366, 277 /// MIPS16 with FPU 278 MipsFpu16 = 0x466, 279 /// Power PC little endian 280 PowerPC = 0x1F0, 281 /// Power PC with floating point support 282 PowerPCFP = 0x1F1, 283 /// MIPS little endian 284 R4000 = 0x166, 285 /// RISC-V 32-bit address space 286 RiscV32 = 0x5032, 287 /// RISC-V 64-bit address space 288 RiscV64 = 0x5064, 289 /// RISC-V 128-bit address space 290 RiscV128 = 0x5128, 291 /// Hitachi SH3 292 SH3 = 0x1A2, 293 /// Hitachi SH3 DSP 294 SH3DSP = 0x1A3, 295 /// Hitachi SH4 296 SH4 = 0x1A6, 297 /// Hitachi SH5 298 SH5 = 0x1A8, 299 /// Thumb 300 Thumb = 0x1C2, 301 /// MIPS little-endian WCE v2 302 WceMipsV2 = 0x169, 303 /// Invalid value 304 Invalid = 0xffff, 305 } 306 307 impl fmt::Display for MachineType { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 309 match *self { 310 MachineType::Invalid => write!(f, "Invalid"), 311 MachineType::Unknown => write!(f, "Unknown"), 312 MachineType::Am33 => write!(f, "Am33"), 313 MachineType::Amd64 => write!(f, "Amd64"), 314 MachineType::Arm => write!(f, "Arm"), 315 MachineType::Arm64 => write!(f, "Arm64"), 316 MachineType::ArmNT => write!(f, "ArmNT"), 317 MachineType::Ebc => write!(f, "Ebc"), 318 MachineType::X86 => write!(f, "X86"), 319 MachineType::Ia64 => write!(f, "Ia64"), 320 MachineType::M32R => write!(f, "M32R"), 321 MachineType::Mips16 => write!(f, "Mips16"), 322 MachineType::MipsFpu => write!(f, "MipsFpu"), 323 MachineType::MipsFpu16 => write!(f, "MipsFpu16"), 324 MachineType::PowerPC => write!(f, "PowerPC"), 325 MachineType::PowerPCFP => write!(f, "PowerPCFP"), 326 MachineType::R4000 => write!(f, "R4000"), 327 MachineType::RiscV32 => write!(f, "RiscV32"), 328 MachineType::RiscV64 => write!(f, "RiscV64"), 329 MachineType::RiscV128 => write!(f, "RiscV128"), 330 MachineType::SH3 => write!(f, "SH3"), 331 MachineType::SH3DSP => write!(f, "SH3DSP"), 332 MachineType::SH4 => write!(f, "SH4"), 333 MachineType::SH5 => write!(f, "SH5"), 334 MachineType::Thumb => write!(f, "Thumb"), 335 MachineType::WceMipsV2 => write!(f, "WceMipsV2"), 336 } 337 } 338 } 339 340 impl From<u16> for MachineType { from(value: u16) -> Self341 fn from(value: u16) -> Self { 342 match value { 343 0xffff => MachineType::Invalid, 344 0x0 => MachineType::Unknown, 345 0x13 => MachineType::Am33, 346 0x8664 => MachineType::Amd64, 347 0x1C0 => MachineType::Arm, 348 0xAA64 => MachineType::Arm64, 349 0x1C4 => MachineType::ArmNT, 350 0xEBC => MachineType::Ebc, 351 0x14C => MachineType::X86, 352 0x200 => MachineType::Ia64, 353 0x9041 => MachineType::M32R, 354 0x266 => MachineType::Mips16, 355 0x366 => MachineType::MipsFpu, 356 0x466 => MachineType::MipsFpu16, 357 0x1F0 => MachineType::PowerPC, 358 0x1F1 => MachineType::PowerPCFP, 359 0x166 => MachineType::R4000, 360 0x5032 => MachineType::RiscV32, 361 0x5064 => MachineType::RiscV64, 362 0x5128 => MachineType::RiscV128, 363 0x1A2 => MachineType::SH3, 364 0x1A3 => MachineType::SH3DSP, 365 0x1A6 => MachineType::SH4, 366 0x1A8 => MachineType::SH5, 367 0x1C2 => MachineType::Thumb, 368 0x169 => MachineType::WceMipsV2, 369 _ => MachineType::Unknown, 370 } 371 } 372 } 373 374 /// Information about a module's contribution to a section. 375 /// `struct SC` in Microsoft's code: 376 /// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/include/dbicommon.h#L42 377 #[derive(Debug, Copy, Clone)] 378 pub struct DBISectionContribution { 379 /// Start offset of the section. 380 pub offset: PdbInternalSectionOffset, 381 /// The size of the contribution, in bytes. 382 pub size: u32, 383 /// The characteristics, which map to the `Characteristics` field of 384 /// the [`IMAGE_SECTION_HEADER`] field in binaries. 385 /// 386 /// [`IMAGE_SECTION_HEADER`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680341(v=vs.85).aspx 387 pub characteristics: u32, 388 /// The index of the module. 389 pub module: u16, 390 /// CRC of the contribution(?) 391 pub data_crc: u32, 392 /// CRC of relocations(?) 393 pub reloc_crc: u32, 394 } 395 396 impl DBISectionContribution { parse(buf: &mut ParseBuffer<'_>) -> Result<Self>397 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> { 398 let section = buf.parse_u16()?; 399 let _padding = buf.parse_u16()?; 400 let offset = buf.parse_u32()?; 401 let size = buf.parse_u32()?; 402 let characteristics = buf.parse_u32()?; 403 let module = buf.parse_u16()?; 404 let _padding = buf.parse_u16()?; 405 406 Ok(DBISectionContribution { 407 offset: PdbInternalSectionOffset { offset, section }, 408 size, 409 characteristics, 410 module, 411 data_crc: buf.parse_u32()?, 412 reloc_crc: buf.parse_u32()?, 413 }) 414 } 415 } 416 417 /// Information about a module parsed from the DBI stream. Named `MODI` in 418 /// the Microsoft PDB source: 419 /// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L1197 420 #[derive(Debug, Copy, Clone)] 421 pub(crate) struct DBIModuleInfo { 422 /// Currently open module. 423 pub opened: u32, 424 /// This module's first section contribution. 425 pub section: DBISectionContribution, 426 /// Flags, expressed as bitfields in the C struct: 427 /// written, EC enabled, unused, tsm 428 /// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L1201-L1204 429 pub flags: u16, 430 /// Stream number of module debug info (syms, lines, fpo). 431 pub stream: StreamIndex, 432 /// Size of local symbols debug info in `stream`. 433 pub symbols_size: u32, 434 /// Size of line number debug info in `stream`. 435 pub lines_size: u32, 436 /// Size of C13 style line number info in `stream`. 437 pub c13_lines_size: u32, 438 /// Number of files contributing to this module. 439 pub files: u16, 440 _padding: u16, 441 /// Used as a pointer into an array of filename indicies in the Microsoft code. 442 pub filename_offsets: u32, 443 /// Source file name index. 444 pub source: u32, 445 /// Path to compiler PDB name index. 446 pub compiler: u32, 447 } 448 449 impl DBIModuleInfo { parse(buf: &mut ParseBuffer<'_>) -> Result<Self>450 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> { 451 Ok(DBIModuleInfo { 452 opened: buf.parse_u32()?, 453 section: DBISectionContribution::parse(buf)?, 454 flags: buf.parse_u16()?, 455 stream: buf.parse()?, 456 symbols_size: buf.parse_u32()?, 457 lines_size: buf.parse_u32()?, 458 c13_lines_size: buf.parse_u32()?, 459 files: buf.parse_u16()?, 460 _padding: buf.parse_u16()?, 461 filename_offsets: buf.parse_u32()?, 462 source: buf.parse_u32()?, 463 compiler: buf.parse_u32()?, 464 }) 465 } 466 } 467 468 /// Represents a module from the DBI stream. 469 /// 470 /// A `Module` is a single item that contributes to the binary, such as an object file or import 471 /// library. 472 /// 473 /// Much of the useful information for a `Module` is stored in a separate stream in the PDB. It can 474 /// be retrieved by calling [`PDB::module_info`](crate::PDB::module_info) with a specific module. 475 #[derive(Debug, Clone)] 476 pub struct Module<'m> { 477 info: DBIModuleInfo, 478 module_name: RawString<'m>, 479 object_file_name: RawString<'m>, 480 } 481 482 impl<'m> Module<'m> { 483 /// The `DBIModuleInfo` from the module info substream in the DBI stream. info(&self) -> &DBIModuleInfo484 pub(crate) fn info(&self) -> &DBIModuleInfo { 485 &self.info 486 } 487 /// The module name. 488 /// 489 /// Usually either a full path to an object file or a string of the form `Import:<dll name>`. module_name(&self) -> Cow<'m, str>490 pub fn module_name(&self) -> Cow<'m, str> { 491 self.module_name.to_string() 492 } 493 /// The object file name. 494 /// 495 /// May be the same as `module_name` for object files passed directly 496 /// to the linker. For modules from static libraries, this is usually 497 /// the full path to the archive. object_file_name(&self) -> Cow<'m, str>498 pub fn object_file_name(&self) -> Cow<'m, str> { 499 self.object_file_name.to_string() 500 } 501 } 502 503 /// A `ModuleIter` iterates over the modules in the DBI section, producing `Module`s. 504 #[derive(Debug)] 505 pub struct ModuleIter<'m> { 506 buf: ParseBuffer<'m>, 507 } 508 509 impl<'m> FallibleIterator for ModuleIter<'m> { 510 type Item = Module<'m>; 511 type Error = Error; 512 next(&mut self) -> result::Result<Option<Self::Item>, Self::Error>513 fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> { 514 // see if we're at EOF 515 if self.buf.is_empty() { 516 return Ok(None); 517 } 518 519 let info = DBIModuleInfo::parse(&mut self.buf)?; 520 let module_name = self.buf.parse_cstring()?; 521 let object_file_name = self.buf.parse_cstring()?; 522 self.buf.align(4)?; 523 Ok(Some(Module { 524 info, 525 module_name, 526 object_file_name, 527 })) 528 } 529 } 530 531 /// The version of the section contribution stream. 532 #[derive(Debug, Copy, Clone, PartialEq)] 533 #[allow(missing_docs)] 534 enum DBISectionContributionStreamVersion { 535 V60, 536 V2, 537 OtherValue(u32), 538 } 539 540 impl From<u32> for DBISectionContributionStreamVersion { from(v: u32) -> Self541 fn from(v: u32) -> Self { 542 const V60: u32 = 0xeffe_0000 + 19_970_605; 543 const V2: u32 = 0xeffe_0000 + 20_140_516; 544 match v { 545 V60 => DBISectionContributionStreamVersion::V60, 546 V2 => DBISectionContributionStreamVersion::V2, 547 _ => DBISectionContributionStreamVersion::OtherValue(v), 548 } 549 } 550 } 551 552 /// A `DBISectionContributionIter` iterates over the section contributions in the DBI section, producing `DBISectionContribution`s. 553 #[derive(Debug)] 554 pub struct DBISectionContributionIter<'c> { 555 buf: ParseBuffer<'c>, 556 version: DBISectionContributionStreamVersion, 557 } 558 559 impl<'c> DBISectionContributionIter<'c> { parse(mut buf: ParseBuffer<'c>) -> Result<Self>560 fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> { 561 let version = buf.parse_u32()?.into(); 562 Ok(Self { buf, version }) 563 } 564 } 565 566 impl<'c> FallibleIterator for DBISectionContributionIter<'c> { 567 type Item = DBISectionContribution; 568 type Error = Error; 569 next(&mut self) -> result::Result<Option<Self::Item>, Self::Error>570 fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> { 571 // see if we're at EOF 572 if self.buf.is_empty() { 573 return Ok(None); 574 } 575 576 let contribution = DBISectionContribution::parse(&mut self.buf)?; 577 if self.version == DBISectionContributionStreamVersion::V2 { 578 self.buf.parse_u32()?; 579 } 580 Ok(Some(contribution)) 581 } 582 } 583 584 /// A `DbgDataHdr`, which contains a series of (optional) MSF stream numbers. 585 #[derive(Debug, Copy, Clone)] 586 pub(crate) struct DBIExtraStreams { 587 // The struct itself is defined at: 588 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L250-L274 589 // It's just an array of stream numbers; `u16`s where 0xffff means "no stream". 590 // 591 // The array indices are: 592 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/langapi/include/pdb.h#L439-L449 593 // We'll map those to fields. 594 // 595 // The struct itself can be truncated. This is an internal struct; we'll treat missing fields as 596 // StreamIndex::none() even if it's a short read, so long as the short read stops on a u16 boundary. 597 pub fpo: StreamIndex, 598 pub exception: StreamIndex, 599 pub fixup: StreamIndex, 600 pub omap_to_src: StreamIndex, 601 pub omap_from_src: StreamIndex, 602 pub section_headers: StreamIndex, 603 pub token_rid_map: StreamIndex, 604 pub xdata: StreamIndex, 605 pub pdata: StreamIndex, 606 pub framedata: StreamIndex, 607 pub original_section_headers: StreamIndex, 608 } 609 610 impl DBIExtraStreams { new(debug_info: &DebugInformation<'_>) -> Result<Self>611 pub(crate) fn new(debug_info: &DebugInformation<'_>) -> Result<Self> { 612 // calculate the location of the extra stream information 613 let header = debug_info.header; 614 let offset = debug_info.header_len 615 + (header.module_list_size 616 + header.section_contribution_size 617 + header.section_map_size 618 + header.file_info_size 619 + header.type_server_map_size 620 + header.mfc_type_server_index 621 + header.ec_substream_size) as usize; 622 623 // seek 624 let mut buf = debug_info.stream.parse_buffer(); 625 buf.take(offset)?; 626 627 // grab that section as bytes 628 let bytes = buf.take(header.debug_header_size as _)?; 629 630 // parse those bytes 631 let mut extra_streams_buf = ParseBuffer::from(bytes); 632 Self::parse(&mut extra_streams_buf) 633 } 634 parse(buf: &mut ParseBuffer<'_>) -> Result<Self>635 pub(crate) fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> { 636 // short reads are okay, as are long reads -- this struct is actually an array 637 // what's _not_ okay are 638 if buf.len() % 2 != 0 { 639 return Err(Error::InvalidStreamLength("DbgDataHdr")); 640 } 641 642 fn next_index(buf: &mut ParseBuffer<'_>) -> Result<StreamIndex> { 643 if buf.is_empty() { 644 Ok(StreamIndex::none()) 645 } else { 646 buf.parse() 647 } 648 } 649 650 Ok(DBIExtraStreams { 651 fpo: next_index(buf)?, 652 exception: next_index(buf)?, 653 fixup: next_index(buf)?, 654 omap_to_src: next_index(buf)?, 655 omap_from_src: next_index(buf)?, 656 section_headers: next_index(buf)?, 657 token_rid_map: next_index(buf)?, 658 xdata: next_index(buf)?, 659 pdata: next_index(buf)?, 660 framedata: next_index(buf)?, 661 original_section_headers: next_index(buf)?, 662 }) 663 } 664 } 665 666 #[cfg(test)] 667 mod tests { 668 use crate::dbi::*; 669 670 #[test] test_dbi_extra_streams()671 fn test_dbi_extra_streams() { 672 let bytes = vec![0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0xff, 0xff, 0x05, 0x06]; 673 674 let mut buf = ParseBuffer::from(bytes.as_slice()); 675 let extra_streams = DBIExtraStreams::parse(&mut buf).expect("parse"); 676 677 // check readback 678 assert_eq!(extra_streams.fpo, StreamIndex::none()); 679 assert_eq!(extra_streams.exception, StreamIndex(0x0201)); 680 assert_eq!(extra_streams.fixup, StreamIndex(0x0403)); 681 assert_eq!(extra_streams.omap_to_src, StreamIndex::none()); 682 assert_eq!(extra_streams.omap_from_src, StreamIndex(0x0605)); 683 684 // check that short reads => StreamIndex::none() 685 assert_eq!(extra_streams.section_headers, StreamIndex::none()); 686 assert_eq!(extra_streams.token_rid_map, StreamIndex::none()); 687 assert_eq!(extra_streams.original_section_headers, StreamIndex::none()); 688 } 689 } 690