1pub use maybe_owned;
45pub use pdb;
46
47mod constants;
48mod error;
49mod type_formatter;
50
51pub use error::Error;
52pub use type_formatter::*;
53
54use constants::*;
55use elsa::sync::FrozenMap;
56use maybe_owned::{MaybeOwned, MaybeOwnedMut};
57use pdb::{
58 AddressMap, DebugInformation, FallibleIterator, FileIndex, IdIndex, IdInformation,
59 ImageSectionHeader, InlineSiteSymbol, Inlinee, LineProgram, Module, ModuleInfo,
60 PdbInternalSectionOffset, PublicSymbol, RawString, Rva, Source, StringTable, SymbolData,
61 SymbolIndex, SymbolIter, SymbolTable, TypeIndex, TypeInformation, PDB,
62};
63use range_collections::range_set::RangeSetRange;
64use range_collections::{RangeSet, RangeSet2};
65use std::cmp::Ordering;
66use std::collections::HashMap;
67use std::fmt::LowerHex;
68use std::mem;
69use std::sync::{Arc, Mutex};
70use std::{borrow::Cow, cell::RefCell, collections::BTreeMap};
71
72type Result<V> = std::result::Result<V, Error>;
73
74pub struct ContextPdbData<'p, 's, S: Source<'s> + Send + 's> {
94 pdb: Mutex<MaybeOwnedMut<'p, PDB<'s, S>>>,
95
96 module_infos: FrozenMap<usize, Box<ModuleInfo<'s>>>,
100
101 address_map: AddressMap<'s>,
102 string_table: Option<StringTable<'s>>,
103 global_symbols: SymbolTable<'s>,
104 debug_info: DebugInformation<'s>,
105 type_info: TypeInformation<'s>,
106 id_info: IdInformation<'s>,
107}
108
109const _: fn() = || {
111 fn assert<T: ?Sized + Send>() {}
112 assert::<ContextPdbData<std::fs::File>>();
114};
115
116impl<'p, 's, S: Source<'s> + Send + 's> ContextPdbData<'p, 's, S> {
117 pub fn try_from_pdb(pdb: PDB<'s, S>) -> Result<Self> {
121 Self::try_from_maybe_owned(MaybeOwnedMut::Owned(pdb))
122 }
123
124 pub fn try_from_pdb_ref(pdb: &'p mut PDB<'s, S>) -> Result<Self> {
129 Self::try_from_maybe_owned(MaybeOwnedMut::Borrowed(pdb))
130 }
131
132 fn try_from_maybe_owned(mut pdb: MaybeOwnedMut<'p, PDB<'s, S>>) -> Result<Self> {
133 let global_symbols = pdb.global_symbols()?;
134 let debug_info = pdb.debug_information()?;
135 let type_info = pdb.type_information()?;
136 let id_info = pdb.id_information()?;
137 let address_map = pdb.address_map()?;
138 let string_table = pdb.string_table().ok();
139
140 Ok(Self {
141 pdb: Mutex::new(pdb),
142 module_infos: FrozenMap::new(),
143 global_symbols,
144 debug_info,
145 type_info,
146 id_info,
147 address_map,
148 string_table,
149 })
150 }
151
152 pub fn make_type_formatter(&self) -> Result<TypeFormatter<'_, 's>> {
154 self.make_type_formatter_with_flags(Default::default())
155 }
156
157 pub fn make_type_formatter_with_flags(
159 &self,
160 flags: TypeFormatterFlags,
161 ) -> Result<TypeFormatter<'_, 's>> {
162 let modules = self.debug_info.modules()?.collect::<Vec<_>>()?;
168
169 Ok(TypeFormatter::new_from_parts(
170 self,
171 modules,
172 &self.debug_info,
173 &self.type_info,
174 &self.id_info,
175 self.string_table.as_ref(),
176 flags,
177 )?)
178 }
179
180 pub fn make_context(&self) -> Result<Context<'_, 's>> {
182 self.make_context_with_formatter_flags(Default::default())
183 }
184
185 pub fn make_context_with_formatter_flags(
187 &self,
188 flags: TypeFormatterFlags,
189 ) -> Result<Context<'_, 's>> {
190 let type_formatter = self.make_type_formatter_with_flags(flags)?;
191 let sections = self.pdb.lock().unwrap().sections()?;
192
193 Context::new_from_parts(
194 self,
195 sections.as_deref().unwrap_or(&[]),
196 &self.address_map,
197 &self.global_symbols,
198 self.string_table.as_ref(),
199 &self.debug_info,
200 MaybeOwned::Owned(type_formatter),
201 )
202 }
203}
204
205impl<'s, S: Source<'s> + Send + 's> ModuleProvider<'s> for ContextPdbData<'_, 's, S> {
206 fn get_module_info(
207 &self,
208 module_index: usize,
209 module: &Module,
210 ) -> std::result::Result<Option<&ModuleInfo<'s>>, pdb::Error> {
211 if let Some(module_info) = self.module_infos.get(&module_index) {
212 return Ok(Some(module_info));
213 }
214
215 let mut pdb = self.pdb.lock().unwrap();
216 Ok(pdb.module_info(module)?.map(|module_info| {
217 self.module_infos
218 .insert(module_index, Box::new(module_info))
219 }))
220 }
221}
222
223#[derive(Clone)]
225pub struct Function {
226 pub start_rva: u32,
228 pub end_rva: Option<u32>,
230 pub name: Option<String>,
234}
235
236#[derive(Clone)]
238pub struct FunctionFrames<'a> {
239 pub start_rva: u32,
241 pub end_rva: Option<u32>,
243 pub frames: Vec<Frame<'a>>,
247}
248
249#[derive(Clone)]
251pub struct Frame<'a> {
252 pub function: Option<String>,
254 pub file: Option<Cow<'a, str>>,
256 pub line: Option<u32>,
259}
260
261pub struct Context<'a, 's> {
263 address_map: &'a AddressMap<'s>,
264 section_contributions: Vec<ModuleSectionContribution>,
265 string_table: Option<&'a StringTable<'s>>,
266 type_formatter: MaybeOwned<'a, TypeFormatter<'a, 's>>,
267 global_functions: Vec<PublicSymbolFunctionOrPlaceholder<'a>>,
272 cache: RefCell<ContextCache<'a, 's>>,
273}
274
275const _: fn() = || {
277 fn assert<T: ?Sized + Send>() {}
278 assert::<Context>();
279};
280
281impl<'a, 's> Context<'a, 's> {
282 #[allow(clippy::too_many_arguments)]
289 pub fn new_from_parts(
290 module_info_provider: &'a (dyn ModuleProvider<'s> + Sync),
291 sections: &[ImageSectionHeader],
292 address_map: &'a AddressMap<'s>,
293 global_symbols: &'a SymbolTable<'s>,
294 string_table: Option<&'a StringTable<'s>>,
295 debug_info: &'a DebugInformation,
296 type_formatter: MaybeOwned<'a, TypeFormatter<'a, 's>>,
297 ) -> Result<Self> {
298 let mut global_functions = Vec::new();
299
300 let mut symbol_iter = global_symbols.iter();
302 while let Some(symbol) = symbol_iter.next()? {
303 if let S_PUB32 | S_PUB32_ST = symbol.raw_kind() {
304 if let Ok(SymbolData::Public(PublicSymbol { name, offset, .. })) = symbol.parse() {
305 if is_executable_section(offset.section, sections) {
306 global_functions.push(PublicSymbolFunctionOrPlaceholder {
307 start_offset: offset,
308 name: Some(name),
309 });
310 }
311 }
312 }
313 }
314
315 let section_contributions =
323 compute_section_contributions(debug_info, sections, &mut global_functions)?;
324
325 for (section_index_zero_based, section) in sections.iter().enumerate() {
328 let section_index = (section_index_zero_based + 1) as u16;
329 if !is_executable_section(section_index, sections) {
330 continue;
331 }
332 let size = section.virtual_size;
333 let section_end_offset = PdbInternalSectionOffset::new(section_index, size);
334 global_functions.push(PublicSymbolFunctionOrPlaceholder {
335 start_offset: section_end_offset,
336 name: None,
337 });
338 }
339
340 global_functions.sort_by_key(|p| {
347 (
348 p.start_offset.section,
349 p.start_offset.offset,
350 p.name.is_none(),
351 )
352 });
353 global_functions.dedup_by_key(|p| p.start_offset);
354
355 Ok(Self {
356 address_map,
357 section_contributions,
358 string_table,
359 type_formatter,
360 global_functions,
361 cache: RefCell::new(ContextCache {
362 module_cache: BasicModuleInfoCache {
363 cache: Default::default(),
364 module_info_provider,
365 },
366 function_line_cache: Default::default(),
367 procedure_cache: Default::default(),
368 extended_module_cache: Default::default(),
369 inline_name_cache: Default::default(),
370 full_rva_list: Default::default(),
371 }),
372 })
373 }
374
375 pub fn function_count(&self) -> usize {
377 self.global_functions.len()
378 }
379
380 pub fn functions(&self) -> FunctionIter<'_, 'a, 's> {
382 let mut cache = self.cache.borrow_mut();
383 let ContextCache {
384 full_rva_list,
385 module_cache,
386 ..
387 } = &mut *cache;
388 let full_rva_list = full_rva_list
389 .get_or_insert_with(|| Arc::new(self.compute_full_rva_list(module_cache)))
390 .clone();
391 FunctionIter {
392 context: self,
393 full_rva_list,
394 cur_index: 0,
395 }
396 }
397
398 pub fn find_function(&self, probe: u32) -> Result<Option<Function>> {
402 let offset = match Rva(probe).to_internal_offset(self.address_map) {
403 Some(offset) => offset,
404 None => return Ok(None),
405 };
406
407 let mut cache = self.cache.borrow_mut();
408 let ContextCache {
409 module_cache,
410 procedure_cache,
411 ..
412 } = &mut *cache;
413
414 let func = match self.lookup_function(offset, module_cache) {
415 Some(func) => func,
416 None => return Ok(None),
417 };
418
419 match func {
420 PublicOrProcedureSymbol::Public(_, _, global_function_index) => {
421 let func = &self.global_functions[global_function_index];
422 let name = func.name.map(|name| name.to_string().to_string());
423 let start_rva = match func.start_offset.to_rva(self.address_map) {
424 Some(rva) => rva.0,
425 None => return Ok(None),
426 };
427 let end_rva = match self.global_functions.get(global_function_index + 1) {
429 Some(next_entry)
430 if next_entry.start_offset.section == func.start_offset.section =>
431 {
432 match next_entry.start_offset.to_rva(self.address_map) {
433 Some(rva) => Some(rva.0),
434 None => return Ok(None),
435 }
436 }
437 _ => None,
438 };
439 Ok(Some(Function {
440 start_rva,
441 end_rva,
442 name,
443 }))
444 }
445 PublicOrProcedureSymbol::Procedure(module_index, _, func) => {
446 let extended_info = procedure_cache.entry(func.offset).or_default();
447 let name = extended_info
448 .get_name(
449 func,
450 &self.type_formatter,
451 &self.global_functions,
452 module_index,
453 )
454 .map(String::from);
455 let start_rva = match func.offset.to_rva(self.address_map) {
456 Some(rva) => rva.0,
457 None => return Ok(None),
458 };
459 let end_rva = start_rva + func.len;
460 Ok(Some(Function {
461 start_rva,
462 end_rva: Some(end_rva),
463 name,
464 }))
465 }
466 }
467 }
468
469 pub fn find_frames(&self, probe: u32) -> Result<Option<FunctionFrames>> {
476 let offset = match Rva(probe).to_internal_offset(self.address_map) {
477 Some(offset) => offset,
478 None => return Ok(None),
479 };
480
481 let mut cache = self.cache.borrow_mut();
482 let ContextCache {
483 module_cache,
484 procedure_cache,
485 function_line_cache,
486 extended_module_cache,
487 inline_name_cache,
488 ..
489 } = &mut *cache;
490
491 let func = match self.lookup_function(offset, module_cache) {
492 Some(func) => func,
493 None => return Ok(None),
494 };
495
496 let (module_index, module_info, func_offset, func_size, func_name, proc_stuff) = match func
504 {
505 PublicOrProcedureSymbol::Public(module_index, module_info, global_function_index) => {
506 let func = &self.global_functions[global_function_index];
507 let func_name = func.name.map(|name| name.to_string().to_string());
508 let size = match self.global_functions.get(global_function_index + 1) {
510 Some(next_entry)
511 if next_entry.start_offset.section == func.start_offset.section =>
512 {
513 Some(next_entry.start_offset.offset - func.start_offset.offset)
514 }
515 _ => None,
516 };
517 (
518 module_index,
519 module_info,
520 func.start_offset,
521 size,
522 func_name,
523 None,
524 )
525 }
526 PublicOrProcedureSymbol::Procedure(module_index, module_info, proc) => {
527 let proc_extended_info = procedure_cache.entry(proc.offset).or_default();
528 let func_name = proc_extended_info
529 .get_name(
530 proc,
531 &self.type_formatter,
532 &self.global_functions,
533 module_index,
534 )
535 .map(String::from);
536 (
537 module_index,
538 Some(module_info),
539 proc.offset,
540 Some(proc.len),
541 func_name,
542 Some((proc, proc_extended_info)),
543 )
544 }
545 };
546
547 let extended_module_info = match module_info {
548 Some(module_info) => Some(
549 extended_module_cache
550 .entry(module_index)
551 .or_insert_with(|| self.compute_extended_module_info(module_info))
552 .as_mut()
553 .map_err(|err| mem::replace(err, Error::ExtendedModuleInfoUnsuccessful))?,
554 ),
555 None => None,
556 };
557
558 let (file, line) = if let Some(ExtendedModuleInfo { line_program, .. }) =
559 &extended_module_info
560 {
561 let function_line_info = function_line_cache.entry(func_offset).or_default();
562 let lines = function_line_info.get_lines(func_offset, line_program)?;
563 let search = match lines.binary_search_by_key(&offset.offset, |li| li.start_offset) {
564 Err(0) => None,
565 Ok(i) => Some(i),
566 Err(i) => Some(i - 1),
567 };
568 match search {
569 Some(index) => {
570 let line_info = &lines[index];
571 (
572 self.resolve_filename(line_program, line_info.file_index),
573 Some(line_info.line_start),
574 )
575 }
576 None => (None, None),
577 }
578 } else {
579 (None, None)
580 };
581
582 let frame = Frame {
583 function: func_name,
584 file,
585 line,
586 };
587
588 let mut frames = vec![frame];
590
591 if let (Some((proc, proc_extended_info)), Some(extended_module_info)) =
592 (proc_stuff, extended_module_info)
593 {
594 let ExtendedModuleInfo {
595 inlinees,
596 line_program,
597 module_info,
598 ..
599 } = extended_module_info;
600 let mut inline_ranges =
601 proc_extended_info.get_inline_ranges(module_info, proc, inlinees)?;
602
603 loop {
604 let current_depth = (frames.len() - 1) as u16;
605
606 let search = inline_ranges.binary_search_by(|range| {
611 if range.call_depth > current_depth {
612 Ordering::Greater
613 } else if range.call_depth < current_depth {
614 Ordering::Less
615 } else if range.start_offset > offset.offset {
616 Ordering::Greater
617 } else if range.end_offset <= offset.offset {
618 Ordering::Less
619 } else {
620 Ordering::Equal
621 }
622 });
623 let (inline_range, remainder) = match search {
624 Ok(index) => (&inline_ranges[index], &inline_ranges[index + 1..]),
625 Err(_) => break,
626 };
627
628 let function = inline_name_cache
629 .entry(inline_range.inlinee)
630 .or_insert_with(|| {
631 self.type_formatter
632 .format_id(module_index, inline_range.inlinee)
633 })
634 .as_ref()
635 .ok()
636 .cloned();
637 let file = inline_range
638 .file_index
639 .and_then(|file_index| self.resolve_filename(line_program, file_index));
640 let line = inline_range.line_start;
641 frames.push(Frame {
642 function,
643 file,
644 line,
645 });
646
647 inline_ranges = remainder;
648 }
649
650 frames.reverse();
652 }
653
654 let start_rva = match func_offset.to_rva(self.address_map) {
655 Some(rva) => rva.0,
656 None => return Ok(None),
657 };
658 let end_rva = func_size.and_then(|size| start_rva.checked_add(size));
659
660 Ok(Some(FunctionFrames {
661 start_rva,
662 end_rva,
663 frames,
664 }))
665 }
666
667 fn compute_full_rva_list(&self, module_cache: &mut BasicModuleInfoCache<'a, 's>) -> Vec<u32> {
668 let mut list = Vec::new();
669 for func in &self.global_functions {
670 if let Some(rva) = func.start_offset.to_rva(self.address_map) {
671 list.push(rva.0);
672 }
673 }
674 for module_index in 0..self.type_formatter.modules().len() {
675 if let Some(BasicModuleInfo { procedures, .. }) =
676 module_cache.get_basic_module_info(self.type_formatter.modules(), module_index)
677 {
678 for proc in procedures {
679 if let Some(rva) = proc.offset.to_rva(self.address_map) {
680 list.push(rva.0);
681 }
682 }
683 }
684 }
685 list.sort_unstable();
686 list.dedup();
687 list
688 }
689
690 fn lookup_function<'m>(
691 &self,
692 offset: PdbInternalSectionOffset,
693 module_cache: &'m mut BasicModuleInfoCache<'a, 's>,
694 ) -> Option<PublicOrProcedureSymbol<'a, 's, 'm>> {
695 let sc_index = match self.section_contributions.binary_search_by(|sc| {
696 if sc.section_index < offset.section {
697 Ordering::Less
698 } else if sc.section_index > offset.section {
699 Ordering::Greater
700 } else if sc.end_offset <= offset.offset {
701 Ordering::Less
702 } else if sc.start_offset > offset.offset {
703 Ordering::Greater
704 } else {
705 Ordering::Equal
706 }
707 }) {
708 Ok(sc_index) => sc_index,
709 Err(_) => {
710 return None;
712 }
713 };
714
715 let sc = &self.section_contributions[sc_index];
716 let basic_module_info =
717 module_cache.get_basic_module_info(self.type_formatter.modules(), sc.module_index);
718
719 let module_info = if let Some(BasicModuleInfo {
720 procedures,
721 module_info,
722 }) = basic_module_info
723 {
724 if let Ok(procedure_index) = procedures.binary_search_by(|p| {
725 if p.offset.section < offset.section {
726 Ordering::Less
727 } else if p.offset.section > offset.section {
728 Ordering::Greater
729 } else if p.offset.offset + p.len <= offset.offset {
730 Ordering::Less
731 } else if p.offset.offset > offset.offset {
732 Ordering::Greater
733 } else {
734 Ordering::Equal
735 }
736 }) {
737 return Some(PublicOrProcedureSymbol::Procedure(
739 sc.module_index,
740 module_info,
741 &procedures[procedure_index],
742 ));
743 }
744 Some(*module_info)
745 } else {
746 None
747 };
748
749 let last_global_function_starting_lte_address = match self
755 .global_functions
756 .binary_search_by_key(&(offset.section, offset.offset), |p| {
757 (p.start_offset.section, p.start_offset.offset)
758 }) {
759 Err(0) => return None,
760 Ok(i) => i,
761 Err(i) => i - 1,
762 };
763 let fun = &self.global_functions[last_global_function_starting_lte_address];
764 debug_assert!(
765 fun.start_offset.section < offset.section
766 || (fun.start_offset.section == offset.section
767 && fun.start_offset.offset <= offset.offset)
768 );
769 if fun.start_offset.section != offset.section {
770 return None;
771 }
772 if fun.start_offset.offset < sc.start_offset {
774 return None;
775 }
776
777 Some(PublicOrProcedureSymbol::Public(
778 sc.module_index,
779 module_info,
780 last_global_function_starting_lte_address,
781 ))
782 }
783
784 fn compute_extended_module_info(
785 &self,
786 module_info: &'a ModuleInfo<'s>,
787 ) -> Result<ExtendedModuleInfo<'a, 's>> {
788 let line_program = module_info.line_program()?;
789
790 let inlinees: BTreeMap<IdIndex, Inlinee> = module_info
791 .inlinees()?
792 .map(|i| Ok((i.index(), i)))
793 .collect()?;
794
795 Ok(ExtendedModuleInfo {
796 module_info,
797 inlinees,
798 line_program,
799 })
800 }
801
802 fn resolve_filename(
803 &self,
804 line_program: &LineProgram,
805 file_index: FileIndex,
806 ) -> Option<Cow<'a, str>> {
807 if let Some(string_table) = self.string_table {
808 if let Ok(file_info) = line_program.get_file_info(file_index) {
809 return file_info.name.to_string_lossy(string_table).ok();
810 }
811 }
812 None
813 }
814}
815
816#[derive(Clone)]
818pub struct FunctionIter<'c, 'a, 's> {
819 context: &'c Context<'a, 's>,
820 full_rva_list: Arc<Vec<u32>>,
821 cur_index: usize,
822}
823
824impl Iterator for FunctionIter<'_, '_, '_> {
825 type Item = Function;
826
827 fn next(&mut self) -> Option<Function> {
828 loop {
829 if self.cur_index >= self.full_rva_list.len() {
830 return None;
831 }
832 let rva = self.full_rva_list[self.cur_index];
833 self.cur_index += 1;
834 if let Ok(Some(fun)) = self.context.find_function(rva) {
835 return Some(fun);
836 }
837 }
838 }
839}
840
841struct ContextCache<'a, 's> {
842 module_cache: BasicModuleInfoCache<'a, 's>,
843 function_line_cache: HashMap<PdbInternalSectionOffset, FunctionLineInfo>,
844 procedure_cache: HashMap<PdbInternalSectionOffset, ExtendedProcedureInfo>,
845 extended_module_cache: BTreeMap<usize, Result<ExtendedModuleInfo<'a, 's>>>,
846 inline_name_cache: BTreeMap<IdIndex, Result<String>>,
847 full_rva_list: Option<Arc<Vec<u32>>>,
848}
849
850struct BasicModuleInfoCache<'a, 's> {
851 cache: HashMap<usize, Option<BasicModuleInfo<'a, 's>>>,
852 module_info_provider: &'a (dyn ModuleProvider<'s> + Sync),
853}
854
855impl<'a, 's> BasicModuleInfoCache<'a, 's> {
856 pub fn get_basic_module_info(
857 &mut self,
858 modules: &[Module<'a>],
859 module_index: usize,
860 ) -> Option<&BasicModuleInfo<'a, 's>> {
861 let module_info_provider = self.module_info_provider;
863
864 self.cache
865 .entry(module_index)
866 .or_insert_with(|| {
867 let module = modules.get(module_index)?;
868 let module_info = module_info_provider
869 .get_module_info(module_index, module)
870 .ok()??;
871 BasicModuleInfo::try_from_module_info(module_info).ok()
872 })
873 .as_ref()
874 }
875}
876
877struct BasicModuleInfo<'a, 's> {
878 module_info: &'a ModuleInfo<'s>,
879 procedures: Vec<ProcedureSymbolFunction<'a>>,
880}
881
882impl<'a, 's> BasicModuleInfo<'a, 's> {
883 pub fn try_from_module_info(
884 module_info: &'a ModuleInfo<'s>,
885 ) -> Result<BasicModuleInfo<'a, 's>> {
886 let mut symbols_iter = module_info.symbols()?;
887 let mut functions = Vec::new();
888 while let Some(symbol) = symbols_iter.next()? {
889 if let S_LPROC32 | S_LPROC32_ST | S_GPROC32 | S_GPROC32_ST | S_LPROC32_ID
890 | S_GPROC32_ID | S_LPROC32_DPC | S_LPROC32_DPC_ID | S_THUNK32 | S_THUNK32_ST
891 | S_SEPCODE = symbol.raw_kind()
892 {
893 match symbol.parse() {
894 Ok(SymbolData::Procedure(proc)) => {
895 if proc.len == 0 {
896 continue;
897 }
898
899 functions.push(ProcedureSymbolFunction {
900 offset: proc.offset,
901 len: proc.len,
902 name: proc.name,
903 symbol_index: symbol.index(),
904 end_symbol_index: proc.end,
905 type_index: proc.type_index,
906 });
907 }
908 Ok(SymbolData::SeparatedCode(data)) => {
909 if data.len == 0 {
910 continue;
911 }
912
913 let (name, type_index) = match functions.last() {
917 Some(proc) if proc.offset == data.parent_offset => {
918 (proc.name, proc.type_index)
919 }
920 _ => continue,
921 };
922
923 functions.push(ProcedureSymbolFunction {
924 offset: data.offset,
925 len: data.len,
926 name,
927 symbol_index: symbol.index(),
928 end_symbol_index: data.end,
929 type_index,
930 });
931 }
932 Ok(SymbolData::Thunk(thunk)) => {
933 if thunk.len == 0 {
934 continue;
935 }
936
937 functions.push(ProcedureSymbolFunction {
940 offset: thunk.offset,
941 len: thunk.len as u32,
942 name: thunk.name,
943 symbol_index: symbol.index(),
944 end_symbol_index: thunk.end,
945 type_index: TypeIndex(0),
946 });
947 }
948 _ => {}
949 }
950 }
951 }
952 functions.sort_by_key(|p| (p.offset.section, p.offset.offset));
956 functions.dedup_by_key(|p| p.offset);
957
958 Ok(BasicModuleInfo {
959 module_info,
960 procedures: functions,
961 })
962 }
963}
964
965#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
967pub struct ModuleSectionContribution {
968 section_index: u16,
969 start_offset: u32,
970 end_offset: u32,
971 module_index: usize,
972}
973
974fn compute_section_contributions(
980 debug_info: &DebugInformation<'_>,
981 sections: &[ImageSectionHeader],
982 placeholder_functions: &mut Vec<PublicSymbolFunctionOrPlaceholder>,
983) -> Result<Vec<ModuleSectionContribution>> {
984 let mut section_contribution_iter = debug_info
985 .section_contributions()?
986 .filter(|sc| Ok(sc.size != 0 && is_executable_section(sc.offset.section, sections)));
987 let mut section_contributions = Vec::new();
988
989 if let Some(first_sc) = section_contribution_iter.next()? {
990 let mut current_combined_sc = ModuleSectionContribution {
991 section_index: first_sc.offset.section,
992 start_offset: first_sc.offset.offset,
993 end_offset: first_sc.offset.offset + first_sc.size,
994 module_index: first_sc.module,
995 };
996 let mut is_executable = is_executable_section(first_sc.offset.section, sections);
997
998 while let Some(sc) = section_contribution_iter.next()? {
1001 let section_index = sc.offset.section;
1002 let start_offset = sc.offset.offset;
1003 let end_offset = start_offset + sc.size;
1004 let module_index = sc.module;
1005 if section_index == current_combined_sc.section_index
1006 && module_index == current_combined_sc.module_index
1007 {
1008 if end_offset < current_combined_sc.end_offset {
1011 return Err(Error::UnorderedSectionContributions(
1012 module_index,
1013 section_index,
1014 ));
1015 }
1016
1017 current_combined_sc.end_offset = end_offset;
1019 } else {
1020 section_contributions.push(current_combined_sc);
1021 current_combined_sc = ModuleSectionContribution {
1022 section_index: sc.offset.section,
1023 start_offset: sc.offset.offset,
1024 end_offset,
1025 module_index: sc.module,
1026 };
1027 is_executable = is_executable_section(sc.offset.section, sections);
1028 }
1029
1030 if is_executable {
1031 placeholder_functions.push(PublicSymbolFunctionOrPlaceholder {
1032 start_offset: sc.offset,
1033 name: None,
1034 });
1035 }
1036 }
1037 section_contributions.push(current_combined_sc);
1038 }
1039
1040 section_contributions.sort_unstable();
1042
1043 if let Some((first_sc, rest)) = section_contributions.split_first() {
1045 let mut prev_sc = first_sc;
1046 for sc in rest {
1047 if sc.section_index == prev_sc.section_index && sc.start_offset < prev_sc.end_offset {
1048 return Err(Error::OverlappingSectionContributions(
1049 sc.section_index,
1050 prev_sc.module_index,
1051 sc.module_index,
1052 ));
1053 }
1054 prev_sc = sc;
1055 }
1056 }
1057
1058 Ok(section_contributions)
1059}
1060
1061fn get_section(section_index: u16, sections: &[ImageSectionHeader]) -> Option<&ImageSectionHeader> {
1063 if section_index == 0 {
1064 None
1065 } else {
1066 sections.get((section_index - 1) as usize)
1067 }
1068}
1069
1070fn is_executable_section(section_index: u16, sections: &[ImageSectionHeader]) -> bool {
1072 match get_section(section_index, sections) {
1073 Some(section) => section.characteristics.execute(), None => false,
1075 }
1076}
1077
1078#[derive(Clone, Debug)]
1081struct PublicSymbolFunctionOrPlaceholder<'s> {
1082 start_offset: PdbInternalSectionOffset,
1087 name: Option<RawString<'s>>,
1090}
1091
1092#[derive(Clone, Debug)]
1093struct ProcedureSymbolFunction<'a> {
1094 offset: PdbInternalSectionOffset,
1096 len: u32,
1098 name: RawString<'a>,
1103 symbol_index: SymbolIndex,
1107 end_symbol_index: SymbolIndex,
1110 type_index: TypeIndex,
1113}
1114
1115enum PublicOrProcedureSymbol<'a, 's, 'm> {
1116 Public(usize, Option<&'a ModuleInfo<'s>>, usize),
1117 Procedure(usize, &'a ModuleInfo<'s>, &'m ProcedureSymbolFunction<'a>),
1118}
1119
1120#[derive(Default)]
1121struct FunctionLineInfo {
1122 lines: Option<Result<Vec<CachedLineInfo>>>,
1123}
1124
1125impl FunctionLineInfo {
1126 fn get_lines(
1127 &mut self,
1128 function_offset: PdbInternalSectionOffset,
1129 line_program: &LineProgram,
1130 ) -> Result<&[CachedLineInfo]> {
1131 let lines = self
1132 .lines
1133 .get_or_insert_with(|| {
1134 let mut iterator = line_program.lines_for_symbol(function_offset);
1135 let mut lines = Vec::new();
1136 let mut next_item = iterator.next()?;
1137 while let Some(line_info) = next_item {
1138 next_item = iterator.next()?;
1139 lines.push(CachedLineInfo {
1140 start_offset: line_info.offset.offset,
1141 file_index: line_info.file_index,
1142 line_start: line_info.line_start,
1143 });
1144 }
1145 Ok(lines)
1146 })
1147 .as_mut()
1148 .map_err(|e| mem::replace(e, Error::ProcedureLinesUnsuccessful))?;
1149 Ok(lines)
1150 }
1151}
1152
1153#[derive(Default)]
1154struct ExtendedProcedureInfo {
1155 name: Option<Option<String>>,
1156 inline_ranges: Option<Result<Vec<InlineRange>>>,
1157}
1158
1159impl ExtendedProcedureInfo {
1160 fn get_name(
1161 &mut self,
1162 proc: &ProcedureSymbolFunction,
1163 type_formatter: &TypeFormatter,
1164 global_functions: &[PublicSymbolFunctionOrPlaceholder],
1165 module_index: usize,
1166 ) -> Option<&str> {
1167 self.name
1168 .get_or_insert_with(|| {
1169 if proc.type_index == TypeIndex(0) && !proc.name.as_bytes().starts_with(b"?") {
1170 if let Ok(public_fun_index) = global_functions
1175 .binary_search_by_key(&(proc.offset.section, proc.offset.offset), |f| {
1176 (f.start_offset.section, f.start_offset.offset)
1177 })
1178 {
1179 if let Some(name) = global_functions[public_fun_index].name {
1180 if name.as_bytes().starts_with(b"?") {
1181 return Some(name.to_string().to_string());
1182 }
1183 }
1184 }
1185 }
1186 type_formatter
1187 .format_function(&proc.name.to_string(), module_index, proc.type_index)
1188 .ok()
1189 })
1190 .as_deref()
1191 }
1192
1193 fn get_inline_ranges(
1194 &mut self,
1195 module_info: &ModuleInfo,
1196 proc: &ProcedureSymbolFunction,
1197 inlinees: &BTreeMap<IdIndex, Inlinee>,
1198 ) -> Result<&[InlineRange]> {
1199 let inline_ranges = self
1200 .inline_ranges
1201 .get_or_insert_with(|| compute_procedure_inline_ranges(module_info, proc, inlinees))
1202 .as_mut()
1203 .map_err(|e| mem::replace(e, Error::ProcedureInlineRangesUnsuccessful))?;
1204 Ok(inline_ranges)
1205 }
1206}
1207
1208fn compute_procedure_inline_ranges(
1209 module_info: &ModuleInfo,
1210 proc: &ProcedureSymbolFunction,
1211 inlinees: &BTreeMap<IdIndex, Inlinee>,
1212) -> Result<Vec<InlineRange>> {
1213 let mut lines = Vec::new();
1214 let mut symbols_iter = module_info.symbols_at(proc.symbol_index)?;
1215 let _proc_sym = symbols_iter.next()?;
1216 while let Some(symbol) = symbols_iter.next()? {
1217 if symbol.index() >= proc.end_symbol_index {
1218 break;
1219 }
1220 if let S_LPROC32 | S_LPROC32_ST | S_GPROC32 | S_GPROC32_ST | S_LPROC32_ID | S_GPROC32_ID
1221 | S_LPROC32_DPC | S_LPROC32_DPC_ID | S_INLINESITE | S_INLINESITE2 = symbol.raw_kind()
1222 {
1223 match symbol.parse() {
1224 Ok(SymbolData::Procedure(p)) => {
1225 symbols_iter.skip_to(p.end)?;
1227 }
1228 Ok(SymbolData::InlineSite(site)) => {
1229 process_inlinee_symbols(
1230 &mut symbols_iter,
1231 inlinees,
1232 proc.offset,
1233 site,
1234 0,
1235 &mut lines,
1236 )?;
1237 }
1238 _ => {}
1239 }
1240 }
1241 }
1242
1243 lines.sort_unstable_by(|r1, r2| {
1244 if r1.call_depth < r2.call_depth {
1245 Ordering::Less
1246 } else if r1.call_depth > r2.call_depth {
1247 Ordering::Greater
1248 } else if r1.start_offset < r2.start_offset {
1249 Ordering::Less
1250 } else if r1.start_offset > r2.start_offset {
1251 Ordering::Greater
1252 } else {
1253 Ordering::Equal
1254 }
1255 });
1256
1257 Ok(lines)
1258}
1259
1260fn process_inlinee_symbols(
1261 symbols_iter: &mut SymbolIter,
1262 inlinees: &BTreeMap<IdIndex, Inlinee>,
1263 proc_offset: PdbInternalSectionOffset,
1264 site: InlineSiteSymbol,
1265 call_depth: u16,
1266 lines: &mut Vec<InlineRange>,
1267) -> Result<RangeSet2<u32>> {
1268 let mut ranges = RangeSet2::empty();
1269 let mut file_index = None;
1270 if let Some(inlinee) = inlinees.get(&site.inlinee) {
1271 let mut iter = inlinee.lines(proc_offset, &site);
1272 while let Ok(Some(line_info)) = iter.next() {
1273 let length = match line_info.length {
1274 Some(0) | None => {
1275 continue;
1276 }
1277 Some(l) => l,
1278 };
1279 let start_offset = line_info.offset.offset;
1280 let end_offset = line_info.offset.offset + length;
1281 lines.push(InlineRange {
1282 start_offset,
1283 end_offset,
1284 call_depth,
1285 inlinee: site.inlinee,
1286 file_index: Some(line_info.file_index),
1287 line_start: Some(line_info.line_start),
1288 });
1289 ranges |= RangeSet::from(start_offset..end_offset);
1290 if file_index.is_none() {
1291 file_index = Some(line_info.file_index);
1292 }
1293 }
1294 }
1295
1296 let mut callee_ranges = RangeSet2::empty();
1297 while let Some(symbol) = symbols_iter.next()? {
1298 if symbol.index() >= site.end {
1299 break;
1300 }
1301 if let S_LPROC32 | S_LPROC32_ST | S_GPROC32 | S_GPROC32_ST | S_LPROC32_ID | S_GPROC32_ID
1302 | S_LPROC32_DPC | S_LPROC32_DPC_ID | S_INLINESITE | S_INLINESITE2 = symbol.raw_kind()
1303 {
1304 match symbol.parse() {
1305 Ok(SymbolData::Procedure(p)) => {
1306 symbols_iter.skip_to(p.end)?;
1308 }
1309 Ok(SymbolData::InlineSite(site)) => {
1310 callee_ranges |= process_inlinee_symbols(
1311 symbols_iter,
1312 inlinees,
1313 proc_offset,
1314 site,
1315 call_depth + 1,
1316 lines,
1317 )?;
1318 }
1319 _ => {}
1320 }
1321 }
1322 }
1323
1324 if !ranges.is_superset(&callee_ranges) {
1325 let missing_ranges: RangeSet2<u32> = &callee_ranges - &ranges;
1327 for range in missing_ranges.iter() {
1328 let (start_offset, end_offset) = match range {
1329 RangeSetRange::Range(r) => (*r.start, *r.end),
1330 other => {
1331 panic!("Unexpected range bounds {:?}", other);
1332 }
1333 };
1334 lines.push(InlineRange {
1335 start_offset,
1336 end_offset,
1337 call_depth,
1338 inlinee: site.inlinee,
1339 file_index,
1340 line_start: None,
1341 });
1342 }
1343 ranges |= missing_ranges;
1344 }
1345
1346 Ok(ranges)
1347}
1348
1349struct ExtendedModuleInfo<'a, 's> {
1350 module_info: &'a ModuleInfo<'s>,
1351 inlinees: BTreeMap<IdIndex, Inlinee<'a>>,
1352 line_program: LineProgram<'a>,
1353}
1354
1355#[derive(Clone, Debug)]
1356struct CachedLineInfo {
1357 pub start_offset: u32,
1358 pub file_index: FileIndex,
1359 pub line_start: u32,
1360}
1361
1362struct HexNum<N: LowerHex>(pub N);
1363
1364impl<N: LowerHex> std::fmt::Debug for HexNum<N> {
1365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1366 LowerHex::fmt(&self.0, f)
1367 }
1368}
1369
1370#[derive(Clone)]
1376struct InlineRange {
1377 pub start_offset: u32,
1380 pub end_offset: u32,
1383 pub call_depth: u16,
1384 pub inlinee: IdIndex,
1385 pub file_index: Option<FileIndex>,
1386 pub line_start: Option<u32>,
1387}
1388
1389impl std::fmt::Debug for InlineRange {
1390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1391 f.debug_struct("InlineRange")
1392 .field("start_offset", &HexNum(self.start_offset))
1393 .field("end_offset", &HexNum(self.end_offset))
1394 .field("call_depth", &self.call_depth)
1395 .field("inlinee", &self.inlinee)
1396 .field("file_index", &self.file_index)
1397 .field("line_start", &self.line_start)
1398 .finish()
1399 }
1400}