pdb_addr2line/
lib.rs

1//! Resolve addresses to function names, and to file name and line number
2//! information, with the help of a PDB file. Inline stacks are supported.
3//!
4//! The API of this crate is intended to be similar to the API of the
5//! [`addr2line` crate](https://docs.rs/addr2line/); the two [`Context`] APIs
6//! have comparable functionality. This crate is for PDB files whereas `addr2line`
7//! is for DWARF data (which is used in ELF and mach-o binaries, for example).
8//!
9//! This crate also has a [`TypeFormatter`] API which can be used to get function signature
10//! strings independently from a [`Context`].
11//!
12//! To create a [`Context`], use [`ContextPdbData`].
13//!
14//! # Example
15//!
16//! ```
17//! use pdb_addr2line::pdb; // (this is a re-export of the pdb crate)
18//!
19//! fn look_up_addresses<'s, S: pdb::Source<'s> + Send + 's>(stream: S, addresses: &[u32]) -> std::result::Result<(), pdb_addr2line::Error> {
20//!     let pdb = pdb::PDB::open(stream)?;
21//!     let context_data = pdb_addr2line::ContextPdbData::try_from_pdb(pdb)?;
22//!     let context = context_data.make_context()?;
23//!
24//!     for address in addresses {
25//!         if let Some(procedure_frames) = context.find_frames(*address)? {
26//!             eprintln!("0x{:x} - {} frames:", address, procedure_frames.frames.len());
27//!             for frame in procedure_frames.frames {
28//!                 let line_str = frame.line.map(|l| format!("{}", l));
29//!                 eprintln!(
30//!                     "     {} at {}:{}",
31//!                     frame.function.as_deref().unwrap_or("<unknown>"),
32//!                     frame.file.as_deref().unwrap_or("??"),
33//!                     line_str.as_deref().unwrap_or("??"),
34//!                 )
35//!             }
36//!         } else {
37//!             eprintln!("{:x} - no frames found", address);
38//!         }
39//!     }
40//!     Ok(())
41//! }
42//! ```
43
44pub 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
74/// Allows to easily create a [`Context`] directly from a [`pdb::PDB`].
75///
76/// ```
77/// # fn wrapper<'s, S: pdb::Source<'s> + Send + 's>(stream: S) -> std::result::Result<(), pdb_addr2line::Error> {
78/// let pdb = pdb::PDB::open(stream)?;
79/// let context_data = pdb_addr2line::ContextPdbData::try_from_pdb(pdb)?;
80/// let context = context_data.make_context()?;
81/// # Ok(())
82/// # }
83/// ```
84///
85/// Implementation note:
86/// It would be nice if a [`Context`] could be created from a [`PDB`] directly, without
87/// going through an intermediate [`ContextPdbData`] object. However, there doesn't
88/// seem to be an easy way to do this, due to certain lifetime dependencies: The
89/// [`Context`] object wants to store certain objects inside itself (mostly for caching)
90/// which have a lifetime dependency on [`pdb::ModuleInfo`], so the [`ModuleInfo`] has to be
91/// owned outside of the [`Context`]. So the [`ContextPdbData`] object acts as that external
92/// [`ModuleInfo`] owner.
93pub struct ContextPdbData<'p, 's, S: Source<'s> + Send + 's> {
94    pdb: Mutex<MaybeOwnedMut<'p, PDB<'s, S>>>,
95
96    /// ModuleInfo objects are stored on this object (outside Context) so that the
97    /// Context can internally store objects which have a lifetime dependency on
98    /// ModuleInfo, such as Inlinees, LinePrograms, and RawStrings from modules.
99    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
109// Assert that `ContextPdbData` is Send.
110const _: fn() = || {
111    fn assert<T: ?Sized + Send>() {}
112    // Use `File` as `S` since it implements `Source` and is `Send`.
113    assert::<ContextPdbData<std::fs::File>>();
114};
115
116impl<'p, 's, S: Source<'s> + Send + 's> ContextPdbData<'p, 's, S> {
117    /// Create a [`ContextPdbData`] from a [`PDB`](pdb::PDB). This parses many of the PDB
118    /// streams and stores them in the [`ContextPdbData`].
119    /// This creator function takes ownership of the pdb object and never gives it back.
120    pub fn try_from_pdb(pdb: PDB<'s, S>) -> Result<Self> {
121        Self::try_from_maybe_owned(MaybeOwnedMut::Owned(pdb))
122    }
123
124    /// Create a [`ContextPdbData`] from a [`PDB`](pdb::PDB). This parses many of the PDB
125    /// streams and stores them in the [`ContextPdbData`].
126    /// This creator function takes an exclusive reference to the pdb object, for consumers
127    /// that want to keep using the pdb object once the `ContextPdbData` object is dropped.
128    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    /// Create a [`TypeFormatter`]. This uses the default [`TypeFormatter`] settings.
153    pub fn make_type_formatter(&self) -> Result<TypeFormatter<'_, 's>> {
154        self.make_type_formatter_with_flags(Default::default())
155    }
156
157    /// Create a [`TypeFormatter`], using the specified [`TypeFormatter`] flags.
158    pub fn make_type_formatter_with_flags(
159        &self,
160        flags: TypeFormatterFlags,
161    ) -> Result<TypeFormatter<'_, 's>> {
162        // Get the list of all modules. This only reads the list, not the actual module
163        // info. To get the module info, you need to call pdb.module_info(&module), and
164        // that's when the actual module stream is read. We use the list of modules so
165        // that we can call pdb.module_info with the right module, which we look up based
166        // on its module_index.
167        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    /// Create a [`Context`]. This uses the default [`TypeFormatter`] settings.
181    pub fn make_context(&self) -> Result<Context<'_, 's>> {
182        self.make_context_with_formatter_flags(Default::default())
183    }
184
185    /// Create a [`Context`], using the specified [`TypeFormatterFlags`].
186    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/// Basic information about a function.
224#[derive(Clone)]
225pub struct Function {
226    /// The start address of the function, as a relative address (rva).
227    pub start_rva: u32,
228    /// The end address of the function, if known.
229    pub end_rva: Option<u32>,
230    /// The function name. `None` if there was an error during stringification.
231    /// If this function is based on a public symbol, the consumer may need to demangle
232    /// ("undecorate") the name. This can be detected based on a leading '?' byte.
233    pub name: Option<String>,
234}
235
236/// The result of an address lookup from [`Context::find_frames`].
237#[derive(Clone)]
238pub struct FunctionFrames<'a> {
239    /// The start address of the function which contained the looked-up address.
240    pub start_rva: u32,
241    /// The end address of the function which contained the looked-up address, if known.
242    pub end_rva: Option<u32>,
243    /// The inline stack at the looked-up address, ordered from inside to outside.
244    /// Always contains at least one entry: the last element is always the function
245    /// which contains the looked-up address.
246    pub frames: Vec<Frame<'a>>,
247}
248
249/// One frame of the inline stack at the looked-up address.
250#[derive(Clone)]
251pub struct Frame<'a> {
252    /// The function name. `None` if there was an error during stringification.
253    pub function: Option<String>,
254    /// The file name, if known.
255    pub file: Option<Cow<'a, str>>,
256    /// The line number, if known. This is the source line inside this function
257    /// that is associated with the instruction at the looked-up address.
258    pub line: Option<u32>,
259}
260
261/// The main API of this crate. Resolves addresses to function information.
262pub 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    /// Contains an entry for hopefully every function in an executable section.
268    /// The entries come from the public function symbols, and from the section
269    /// contributions: We create an unnamed "placeholder" entry for each section
270    /// contribution.
271    global_functions: Vec<PublicSymbolFunctionOrPlaceholder<'a>>,
272    cache: RefCell<ContextCache<'a, 's>>,
273}
274
275// Assert that `Context` is Send.
276const _: fn() = || {
277    fn assert<T: ?Sized + Send>() {}
278    assert::<Context>();
279};
280
281impl<'a, 's> Context<'a, 's> {
282    /// Create a [`Context`] manually. Most consumers will want to use
283    /// [`ContextPdbData::make_context`] instead.
284    ///
285    /// However, if you interact with a PDB directly and parse some of its contents
286    /// for other uses, you may want to call this method in order to avoid overhead
287    /// from repeatedly parsing the same streams.
288    #[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        // Start with the public function symbols.
301        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        // Read the section contributions. This will let us find the right module
316        // based on the PdbSectionInternalOffset that corresponds to the looked-up
317        // address. This allows reading module info on demand.
318        // The section contributions also give us more function start addresses. We
319        // create placeholder symbols for them so we don't account missing functions to
320        // the nearest public function, and so that we can find line information for
321        // those missing functions if present.
322        let section_contributions =
323            compute_section_contributions(debug_info, sections, &mut global_functions)?;
324
325        // Add a few more placeholder entries for the end addresses of executable sections.
326        // These act as terminator addresses for the last function in a section.
327        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        // Sort and de-duplicate, so that we can use binary search during lookup.
341        // If we have both a public symbol and a placeholder symbol at the same offset,
342        // make it so that the symbol with name comes first, so that we keep it during
343        // the deduplication.
344        // If there are multiple symbols at the same address, we want to keep the first
345        // one, so we use a stable sort.
346        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    /// The number of functions found in public symbols.
376    pub fn function_count(&self) -> usize {
377        self.global_functions.len()
378    }
379
380    /// Iterate over all functions in the modules.
381    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    /// Find the function whose code contains the provided address.
399    /// The return value only contains the function name and the rva range, but
400    /// no file or line information.
401    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                // Get the end address from the address of the next entry in the global function list.
428                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    /// Find information about the source code which generated the instruction at the
470    /// provided address. This information includes the function name, file name and
471    /// line number, of the containing procedure and of any functions that were inlined
472    /// into the procedure by the compiler, at that address.
473    ///
474    /// A lot of information is cached so that repeated calls are fast.
475    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        // We can have a pretty wild mix of available information, depending on what's in
497        // the PDB file.
498        //  - Some PDBs have everything.
499        //  - Some PDBs only have public symbols and no modules at all, so no procedures
500        //    and no file / line info.
501        //  - Some PDBs have public symbols and modules, but the modules only have file /
502        //    line info and no procedures.
503        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                // Get the function size from the address of the next entry in the global function list.
509                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        // Ordered outside to inside, until just before the end of this function.
589        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                // Look up (offset.offset, current_depth) in inline_ranges.
607                // `inlined_addresses` is sorted in "breadth-first traversal order", i.e.
608                // by `call_depth` first, and then by `start_offset`. See the comment at
609                // the sort call for more information about why.
610                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            // Now order from inside to outside.
651            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                // The requested address is not present in any section contribution.
711                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                // Found a procedure at the requested offset.
738                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        // No procedure was found at this offset in the module that the section
750        // contribution pointed us at.
751        // This is not uncommon.
752        // Fall back to the public symbols.
753
754        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        // Ignore symbols outside the section contribution.
773        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/// An iterator over all functions in a [`Context`].
817#[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        // TODO: 2021 edition
862        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                        // SeparatedCode references another procedure with data.parent_offset.
914                        // Usually the SeparatedCode symbol comes right after the referenced symbol.
915                        // Take the name and type_index from the referenced procedure.
916                        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                        // Treat thunks as procedures. This isn't perfectly accurate but it
938                        // doesn't cause any harm.
939                        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        // Sort and de-duplicate, so that we can use binary search during lookup.
953        // Use a stable sort: if there are multiple symbols at the same address,
954        // we want to keep the first one.
955        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/// The order of the fields matters for the lexicographical sort.
966#[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
974/// Returns an array of non-overlapping `ModuleSectionContribution` objects,
975/// sorted by section and then by start offset.
976/// Contributions from the same module to the same section are combined into
977/// one contiguous contribution. The hope is that there is no interleaving,
978/// and this function returns an error if any interleaving is detected.
979fn 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        // Assume that section contributions from the same section and module are
999        // sorted and non-interleaved.
1000        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                // Enforce ordered contributions. If you find a pdb where this errors out,
1009                // please file an issue.
1010                if end_offset < current_combined_sc.end_offset {
1011                    return Err(Error::UnorderedSectionContributions(
1012                        module_index,
1013                        section_index,
1014                    ));
1015                }
1016
1017                // Combine with current section contribution.
1018                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    // Sort. This sorts by section index first, and then start offset within the section.
1041    section_contributions.sort_unstable();
1042
1043    // Enforce no overlap. If you encounter a PDB where this errors out, please file an issue.
1044    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
1061/// section_index is a 1-based index from PdbInternalSectionOffset.
1062fn 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
1070/// section_index is a 1-based index from PdbInternalSectionOffset.
1071fn is_executable_section(section_index: u16, sections: &[ImageSectionHeader]) -> bool {
1072    match get_section(section_index, sections) {
1073        Some(section) => section.characteristics.execute(), // TODO: should this use .executable()?
1074        None => false,
1075    }
1076}
1077
1078/// Offset and name of a function from a public symbol, or from a placeholder symbol from
1079/// the section contributions.
1080#[derive(Clone, Debug)]
1081struct PublicSymbolFunctionOrPlaceholder<'s> {
1082    /// The address at which this function starts, as a section internal offset. The end
1083    /// address for global function symbols is not known. During symbol lookup, if the address
1084    /// is not covered by a procedure symbol (for those, the end addresses are known), then
1085    /// we assume that functions with no end address cover the range up to the next function.
1086    start_offset: PdbInternalSectionOffset,
1087    /// The symbol name of the public symbol. This is the mangled ("decorated") function signature.
1088    /// None if this is a placeholder.
1089    name: Option<RawString<'s>>,
1090}
1091
1092#[derive(Clone, Debug)]
1093struct ProcedureSymbolFunction<'a> {
1094    /// The address at which this function starts, as a section internal offset.
1095    offset: PdbInternalSectionOffset,
1096    /// The length of this function, in bytes, beginning from start_offset.
1097    len: u32,
1098    /// The symbol name. If type_index is 0, then this can be the mangled ("decorated")
1099    /// function signature from a PublicSymbol or from a Thunk. If type_index is non-zero,
1100    /// name is just the function name, potentially including class scope and namespace,
1101    /// but no args. The args are then found in the type.
1102    name: RawString<'a>,
1103    /// The index of the ProcedureSymbol. This allows starting a symbol iteration
1104    /// cheaply from this symbol, for example to find subsequent symbols about
1105    /// inlines in this procedure.
1106    symbol_index: SymbolIndex,
1107    /// The index of the symbol that ends this procedure. This is where the symbol
1108    /// iteration should stop.
1109    end_symbol_index: SymbolIndex,
1110    /// The type of this procedure, or 0. This is needed to get the arguments for the
1111    /// function signature.
1112    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                    // We have no type, so proc.name might be an argument-less string.
1171                    // If we have a public symbol at this address which is a decorated name
1172                    // (starts with a '?'), prefer to use that because it'll usually include
1173                    // the arguments.
1174                    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                    // This is a nested procedure. Skip it.
1226                    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                    // This is a nested procedure. Skip it.
1307                    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        // Workaround bad debug info.
1326        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/// A contiguous address range covering a line record inside an
1371/// inlined function call. These are meaningful in the context of the
1372/// outer function which contains these inline calls; specifically, the
1373/// offsets are expressed relative to the same section that the outer
1374/// function is in.
1375#[derive(Clone)]
1376struct InlineRange {
1377    /// The section-internal offset of the start of the range,
1378    /// relative to the section that the outer function is in.
1379    pub start_offset: u32,
1380    /// The section-internal offset of the end of the range,
1381    /// relative to the section that the outer function is in.
1382    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}