framehop/
unwinder.rs

1use alloc::string::String;
2use alloc::sync::Arc;
3use alloc::vec::Vec;
4use fallible_iterator::FallibleIterator;
5use gimli::{EndianSlice, LittleEndian};
6
7use crate::arch::Arch;
8use crate::cache::{AllocationPolicy, Cache};
9use crate::dwarf::{DwarfCfiIndex, DwarfUnwinder, DwarfUnwinding, UnwindSectionType};
10use crate::error::{Error, UnwinderError};
11use crate::instruction_analysis::InstructionAnalysis;
12
13#[cfg(feature = "macho")]
14use crate::macho::{
15    CompactUnwindInfoUnwinder, CompactUnwindInfoUnwinding, CuiUnwindResult, TextBytes,
16};
17#[cfg(feature = "pe")]
18use crate::pe::{DataAtRvaRange, PeUnwinding};
19use crate::rule_cache::CacheResult;
20use crate::unwind_result::UnwindResult;
21use crate::unwind_rule::UnwindRule;
22use crate::FrameAddress;
23
24use core::marker::PhantomData;
25use core::ops::{Deref, Range};
26use core::sync::atomic::{AtomicU16, Ordering};
27
28/// Unwinder is the trait that each CPU architecture's concrete unwinder type implements.
29/// This trait's methods are what let you do the actual unwinding.
30pub trait Unwinder: Clone {
31    /// The unwind registers type for the targeted CPU architecture.
32    type UnwindRegs;
33
34    /// The unwind cache for the targeted CPU architecture.
35    /// This is an associated type because the cache stores unwind rules, whose concrete
36    /// type depends on the CPU arch, and because the cache can support different allocation
37    /// policies.
38    type Cache;
39
40    /// The module type. This is an associated type because the concrete type varies
41    /// depending on the type you use to give the module access to the unwind section data.
42    type Module;
43
44    /// Add a module that's loaded in the profiled process. This is how you provide unwind
45    /// information and address ranges.
46    ///
47    /// This should be called whenever a new module is loaded into the process.
48    fn add_module(&mut self, module: Self::Module);
49
50    /// Remove a module that was added before using `add_module`, keyed by the start
51    /// address of that module's address range. If no match is found, the call is ignored.
52    /// This should be called whenever a module is unloaded from the process.
53    fn remove_module(&mut self, module_avma_range_start: u64);
54
55    /// Returns the highest code address that is known in this process based on the module
56    /// address ranges. Returns 0 if no modules have been added.
57    ///
58    /// This method can be used together with
59    /// [`PtrAuthMask::from_max_known_address`](crate::aarch64::PtrAuthMask::from_max_known_address)
60    /// to make an educated guess at a pointer authentication mask for Aarch64 return addresses.
61    fn max_known_code_address(&self) -> u64;
62
63    /// Unwind a single frame, to recover return address and caller register values.
64    /// This is the main entry point for unwinding.
65    fn unwind_frame<F>(
66        &self,
67        address: FrameAddress,
68        regs: &mut Self::UnwindRegs,
69        cache: &mut Self::Cache,
70        read_stack: &mut F,
71    ) -> Result<Option<u64>, Error>
72    where
73        F: FnMut(u64) -> Result<u64, ()>;
74
75    /// Return an iterator that unwinds frame by frame until the end of the stack is found.
76    fn iter_frames<'u, 'c, 'r, F>(
77        &'u self,
78        pc: u64,
79        regs: Self::UnwindRegs,
80        cache: &'c mut Self::Cache,
81        read_stack: &'r mut F,
82    ) -> UnwindIterator<'u, 'c, 'r, Self, F>
83    where
84        F: FnMut(u64) -> Result<u64, ()>,
85    {
86        UnwindIterator::new(self, pc, regs, cache, read_stack)
87    }
88}
89
90/// An iterator for unwinding the entire stack, starting from the initial register values.
91///
92/// The first yielded frame is the instruction pointer. Subsequent addresses are return
93/// addresses.
94///
95/// This iterator attempts to detect if stack unwinding completed successfully, or if the
96/// stack was truncated prematurely. If it thinks that it successfully found the root
97/// function, it will complete with `Ok(None)`, otherwise it will complete with `Err(...)`.
98/// However, the detection does not work in all cases, so you should expect `Err(...)` to
99/// be returned even during normal operation. As a result, it is not recommended to use
100/// this iterator as a `FallibleIterator`, because you might lose the entire stack if the
101/// last iteration returns `Err(...)`.
102///
103/// Lifetimes:
104///
105///  - `'u`: The lifetime of the [`Unwinder`].
106///  - `'c`: The lifetime of the unwinder cache.
107///  - `'r`: The lifetime of the exclusive access to the `read_stack` callback.
108pub struct UnwindIterator<'u, 'c, 'r, U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> {
109    unwinder: &'u U,
110    state: UnwindIteratorState,
111    regs: U::UnwindRegs,
112    cache: &'c mut U::Cache,
113    read_stack: &'r mut F,
114}
115
116enum UnwindIteratorState {
117    Initial(u64),
118    Unwinding(FrameAddress),
119    Done,
120}
121
122impl<'u, 'c, 'r, U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> UnwindIterator<'u, 'c, 'r, U, F> {
123    /// Create a new iterator. You'd usually use [`Unwinder::iter_frames`] instead.
124    pub fn new(
125        unwinder: &'u U,
126        pc: u64,
127        regs: U::UnwindRegs,
128        cache: &'c mut U::Cache,
129        read_stack: &'r mut F,
130    ) -> Self {
131        Self {
132            unwinder,
133            state: UnwindIteratorState::Initial(pc),
134            regs,
135            cache,
136            read_stack,
137        }
138    }
139}
140
141impl<U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> UnwindIterator<'_, '_, '_, U, F> {
142    /// Yield the next frame in the stack.
143    ///
144    /// The first frame is `Ok(Some(FrameAddress::InstructionPointer(...)))`.
145    /// Subsequent frames are `Ok(Some(FrameAddress::ReturnAddress(...)))`.
146    ///
147    /// If a root function has been reached, this iterator completes with `Ok(None)`.
148    /// Otherwise it completes with `Err(...)`, usually indicating that a certain stack
149    /// address could not be read.
150    #[allow(clippy::should_implement_trait)]
151    pub fn next(&mut self) -> Result<Option<FrameAddress>, Error> {
152        let next = match self.state {
153            UnwindIteratorState::Initial(pc) => {
154                self.state = UnwindIteratorState::Unwinding(FrameAddress::InstructionPointer(pc));
155                return Ok(Some(FrameAddress::InstructionPointer(pc)));
156            }
157            UnwindIteratorState::Unwinding(address) => {
158                self.unwinder
159                    .unwind_frame(address, &mut self.regs, self.cache, self.read_stack)?
160            }
161            UnwindIteratorState::Done => return Ok(None),
162        };
163        match next {
164            Some(return_address) => {
165                let return_address = FrameAddress::from_return_address(return_address)
166                    .ok_or(Error::ReturnAddressIsNull)?;
167                self.state = UnwindIteratorState::Unwinding(return_address);
168                Ok(Some(return_address))
169            }
170            None => {
171                self.state = UnwindIteratorState::Done;
172                Ok(None)
173            }
174        }
175    }
176}
177
178impl<U: Unwinder, F: FnMut(u64) -> Result<u64, ()>> FallibleIterator
179    for UnwindIterator<'_, '_, '_, U, F>
180{
181    type Item = FrameAddress;
182    type Error = Error;
183
184    fn next(&mut self) -> Result<Option<FrameAddress>, Error> {
185        self.next()
186    }
187}
188
189/// This global generation counter makes it so that the cache can be shared
190/// between multiple unwinders.
191/// This is a u16, so if you make it wrap around by adding / removing modules
192/// more than 65535 times, then you risk collisions in the cache; meaning:
193/// unwinding might not work properly if an old unwind rule was found in the
194/// cache for the same address and the same (pre-wraparound) modules_generation.
195static GLOBAL_MODULES_GENERATION: AtomicU16 = AtomicU16::new(0);
196
197fn next_global_modules_generation() -> u16 {
198    GLOBAL_MODULES_GENERATION.fetch_add(1, Ordering::Relaxed)
199}
200
201cfg_if::cfg_if! {
202    if #[cfg(all(feature = "macho", feature = "pe"))] {
203        pub trait Unwinding:
204            Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding + PeUnwinding {}
205        impl<T: Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding + PeUnwinding>
206            Unwinding for T {}
207    } else if #[cfg(feature = "macho")] {
208        pub trait Unwinding:
209            Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding {}
210        impl<T: Arch + DwarfUnwinding + InstructionAnalysis + CompactUnwindInfoUnwinding> Unwinding for T {}
211    } else if #[cfg(feature = "pe")] {
212        pub trait Unwinding:
213            Arch + DwarfUnwinding + InstructionAnalysis  + PeUnwinding {}
214        impl<T: Arch + DwarfUnwinding + InstructionAnalysis + PeUnwinding> Unwinding for T {}
215    } else {
216        pub trait Unwinding: Arch + DwarfUnwinding + InstructionAnalysis {}
217        impl<T: Arch + DwarfUnwinding + InstructionAnalysis> Unwinding for T {}
218    }
219}
220
221pub struct UnwinderInternal<D, A, P> {
222    /// sorted by avma_range.start
223    modules: Vec<Module<D>>,
224    /// Incremented every time modules is changed.
225    modules_generation: u16,
226    _arch: PhantomData<A>,
227    _allocation_policy: PhantomData<P>,
228}
229
230impl<D, A, P> Default for UnwinderInternal<D, A, P> {
231    fn default() -> Self {
232        Self::new()
233    }
234}
235
236impl<D, A, P> Clone for UnwinderInternal<D, A, P> {
237    fn clone(&self) -> Self {
238        Self {
239            modules: self.modules.clone(),
240            modules_generation: self.modules_generation,
241            _arch: PhantomData,
242            _allocation_policy: PhantomData,
243        }
244    }
245}
246
247impl<D, A, P> UnwinderInternal<D, A, P> {
248    pub fn new() -> Self {
249        Self {
250            modules: Vec::new(),
251            modules_generation: next_global_modules_generation(),
252            _arch: PhantomData,
253            _allocation_policy: PhantomData,
254        }
255    }
256}
257
258impl<D: Deref<Target = [u8]>, A: Unwinding, P: AllocationPolicy> UnwinderInternal<D, A, P> {
259    pub fn add_module(&mut self, module: Module<D>) {
260        let insertion_index = match self
261            .modules
262            .binary_search_by_key(&module.avma_range.start, |module| module.avma_range.start)
263        {
264            Ok(i) => i, // unexpected
265            Err(i) => i,
266        };
267        self.modules.insert(insertion_index, module);
268        self.modules_generation = next_global_modules_generation();
269    }
270
271    pub fn remove_module(&mut self, module_address_range_start: u64) {
272        if let Ok(index) = self
273            .modules
274            .binary_search_by_key(&module_address_range_start, |module| {
275                module.avma_range.start
276            })
277        {
278            self.modules.remove(index);
279            self.modules_generation = next_global_modules_generation();
280        };
281    }
282
283    pub fn max_known_code_address(&self) -> u64 {
284        self.modules.last().map_or(0, |m| m.avma_range.end)
285    }
286
287    fn find_module_for_address(&self, address: u64) -> Option<(usize, u32)> {
288        let (module_index, module) = match self
289            .modules
290            .binary_search_by_key(&address, |m| m.avma_range.start)
291        {
292            Ok(i) => (i, &self.modules[i]),
293            Err(insertion_index) => {
294                if insertion_index == 0 {
295                    // address is before first known module
296                    return None;
297                }
298                let i = insertion_index - 1;
299                let module = &self.modules[i];
300                if module.avma_range.end <= address {
301                    // address is after this module
302                    return None;
303                }
304                (i, module)
305            }
306        };
307        if address < module.base_avma {
308            // Invalid base address
309            return None;
310        }
311        let relative_address = u32::try_from(address - module.base_avma).ok()?;
312        Some((module_index, relative_address))
313    }
314
315    fn with_cache<F, G>(
316        &self,
317        address: FrameAddress,
318        regs: &mut A::UnwindRegs,
319        cache: &mut Cache<A::UnwindRule, P>,
320        read_stack: &mut F,
321        callback: G,
322    ) -> Result<Option<u64>, Error>
323    where
324        F: FnMut(u64) -> Result<u64, ()>,
325        G: FnOnce(
326            &Module<D>,
327            FrameAddress,
328            u32,
329            &mut A::UnwindRegs,
330            &mut Cache<A::UnwindRule, P>,
331            &mut F,
332        ) -> Result<UnwindResult<A::UnwindRule>, UnwinderError>,
333    {
334        let lookup_address = address.address_for_lookup();
335        let is_first_frame = !address.is_return_address();
336        let cache_handle = match cache
337            .rule_cache
338            .lookup(lookup_address, self.modules_generation)
339        {
340            CacheResult::Hit(unwind_rule) => {
341                return unwind_rule.exec(is_first_frame, regs, read_stack);
342            }
343            CacheResult::Miss(handle) => handle,
344        };
345
346        let unwind_rule = match self.find_module_for_address(lookup_address) {
347            None => A::UnwindRule::fallback_rule(),
348            Some((module_index, relative_lookup_address)) => {
349                let module = &self.modules[module_index];
350                match callback(
351                    module,
352                    address,
353                    relative_lookup_address,
354                    regs,
355                    cache,
356                    read_stack,
357                ) {
358                    Ok(UnwindResult::ExecRule(rule)) => rule,
359                    Ok(UnwindResult::Uncacheable(return_address)) => {
360                        return Ok(Some(return_address))
361                    }
362                    Err(_err) => {
363                        // eprintln!("Unwinder error: {}", err);
364                        A::UnwindRule::fallback_rule()
365                    }
366                }
367            }
368        };
369        cache.rule_cache.insert(cache_handle, unwind_rule);
370        unwind_rule.exec(is_first_frame, regs, read_stack)
371    }
372
373    pub fn unwind_frame<F>(
374        &self,
375        address: FrameAddress,
376        regs: &mut A::UnwindRegs,
377        cache: &mut Cache<A::UnwindRule, P>,
378        read_stack: &mut F,
379    ) -> Result<Option<u64>, Error>
380    where
381        F: FnMut(u64) -> Result<u64, ()>,
382    {
383        self.with_cache(address, regs, cache, read_stack, Self::unwind_frame_impl)
384    }
385
386    fn unwind_frame_impl<F>(
387        module: &Module<D>,
388        address: FrameAddress,
389        rel_lookup_address: u32,
390        regs: &mut A::UnwindRegs,
391        cache: &mut Cache<A::UnwindRule, P>,
392        read_stack: &mut F,
393    ) -> Result<UnwindResult<A::UnwindRule>, UnwinderError>
394    where
395        F: FnMut(u64) -> Result<u64, ()>,
396    {
397        let is_first_frame = !address.is_return_address();
398        let unwind_result = match &*module.unwind_data {
399            #[cfg(feature = "macho")]
400            ModuleUnwindDataInternal::CompactUnwindInfoAndEhFrame {
401                unwind_info,
402                eh_frame,
403                stubs_svma: stubs,
404                stub_helper_svma: stub_helper,
405                base_addresses,
406                text_data,
407            } => {
408                // eprintln!("unwinding with cui and eh_frame in module {}", module.name);
409                let text_bytes = text_data.as_ref().and_then(|data| {
410                    let offset_from_base =
411                        u32::try_from(data.svma_range.start.checked_sub(module.base_svma)?).ok()?;
412                    Some(TextBytes::new(offset_from_base, &data.bytes[..]))
413                });
414                let stubs_range = if let Some(stubs_range) = stubs {
415                    (
416                        (stubs_range.start - module.base_svma) as u32,
417                        (stubs_range.end - module.base_svma) as u32,
418                    )
419                } else {
420                    (0, 0)
421                };
422                let stub_helper_range = if let Some(stub_helper_range) = stub_helper {
423                    (
424                        (stub_helper_range.start - module.base_svma) as u32,
425                        (stub_helper_range.end - module.base_svma) as u32,
426                    )
427                } else {
428                    (0, 0)
429                };
430                let mut unwinder = CompactUnwindInfoUnwinder::<A>::new(
431                    &unwind_info[..],
432                    text_bytes,
433                    stubs_range,
434                    stub_helper_range,
435                );
436
437                let unwind_result = unwinder.unwind_frame(rel_lookup_address, is_first_frame)?;
438                match unwind_result {
439                    CuiUnwindResult::ExecRule(rule) => UnwindResult::ExecRule(rule),
440                    CuiUnwindResult::NeedDwarf(fde_offset) => {
441                        let eh_frame_data =
442                            eh_frame.as_deref().ok_or(UnwinderError::NoDwarfData)?;
443                        let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
444                            EndianSlice::new(eh_frame_data, LittleEndian),
445                            UnwindSectionType::EhFrame,
446                            None,
447                            &mut cache.gimli_unwind_context,
448                            base_addresses.clone(),
449                            module.base_svma,
450                        );
451                        dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
452                            regs,
453                            is_first_frame,
454                            rel_lookup_address,
455                            fde_offset,
456                            read_stack,
457                        )?
458                    }
459                }
460            }
461            ModuleUnwindDataInternal::EhFrameHdrAndEhFrame {
462                eh_frame_hdr,
463                eh_frame,
464                base_addresses,
465            } => {
466                let eh_frame_hdr_data = &eh_frame_hdr[..];
467                let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
468                    EndianSlice::new(eh_frame, LittleEndian),
469                    UnwindSectionType::EhFrame,
470                    Some(eh_frame_hdr_data),
471                    &mut cache.gimli_unwind_context,
472                    base_addresses.clone(),
473                    module.base_svma,
474                );
475                let fde_offset = dwarf_unwinder
476                    .get_fde_offset_for_relative_address(rel_lookup_address)
477                    .ok_or(UnwinderError::EhFrameHdrCouldNotFindAddress)?;
478                dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
479                    regs,
480                    is_first_frame,
481                    rel_lookup_address,
482                    fde_offset,
483                    read_stack,
484                )?
485            }
486            ModuleUnwindDataInternal::DwarfCfiIndexAndEhFrame {
487                index,
488                eh_frame,
489                base_addresses,
490            } => {
491                let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
492                    EndianSlice::new(eh_frame, LittleEndian),
493                    UnwindSectionType::EhFrame,
494                    None,
495                    &mut cache.gimli_unwind_context,
496                    base_addresses.clone(),
497                    module.base_svma,
498                );
499                let fde_offset = index
500                    .fde_offset_for_relative_address(rel_lookup_address)
501                    .ok_or(UnwinderError::DwarfCfiIndexCouldNotFindAddress)?;
502                dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
503                    regs,
504                    is_first_frame,
505                    rel_lookup_address,
506                    fde_offset,
507                    read_stack,
508                )?
509            }
510            ModuleUnwindDataInternal::DwarfCfiIndexAndDebugFrame {
511                index,
512                debug_frame,
513                base_addresses,
514            } => {
515                let mut dwarf_unwinder = DwarfUnwinder::<_, A, _>::new(
516                    EndianSlice::new(debug_frame, LittleEndian),
517                    UnwindSectionType::DebugFrame,
518                    None,
519                    &mut cache.gimli_unwind_context,
520                    base_addresses.clone(),
521                    module.base_svma,
522                );
523                let fde_offset = index
524                    .fde_offset_for_relative_address(rel_lookup_address)
525                    .ok_or(UnwinderError::DwarfCfiIndexCouldNotFindAddress)?;
526                dwarf_unwinder.unwind_frame_with_fde::<_, P::GimliEvaluationStorage<_>>(
527                    regs,
528                    is_first_frame,
529                    rel_lookup_address,
530                    fde_offset,
531                    read_stack,
532                )?
533            }
534            #[cfg(feature = "pe")]
535            ModuleUnwindDataInternal::PeUnwindInfo {
536                pdata,
537                rdata,
538                xdata,
539                text,
540            } => <A as PeUnwinding>::unwind_frame(
541                crate::pe::PeSections {
542                    pdata,
543                    rdata: rdata.as_ref(),
544                    xdata: xdata.as_ref(),
545                    text: text.as_ref(),
546                },
547                rel_lookup_address,
548                regs,
549                is_first_frame,
550                read_stack,
551            )?,
552            ModuleUnwindDataInternal::None => return Err(UnwinderError::NoModuleUnwindData),
553        };
554        Ok(unwind_result)
555    }
556}
557
558/// The unwind data that should be used when unwinding addresses inside this module.
559/// Unwind data describes how to recover register values of the caller frame.
560///
561/// The type of unwind information you use depends on the platform and what's available
562/// in the binary.
563///
564/// Type arguments:
565///
566///  - `D`: The type for unwind section data. This allows carrying owned data on the
567///    module, e.g. `Vec<u8>`. But it could also be a wrapper around mapped memory from
568///    a file or a different process, for example. It just needs to provide a slice of
569///    bytes via its `Deref` implementation.
570enum ModuleUnwindDataInternal<D> {
571    /// Used on macOS, with mach-O binaries. Compact unwind info is in the `__unwind_info`
572    /// section and is sometimes supplemented with DWARF CFI information in the `__eh_frame`
573    /// section. `__stubs` and `__stub_helper` ranges are used by the unwinder.
574    #[cfg(feature = "macho")]
575    CompactUnwindInfoAndEhFrame {
576        unwind_info: D,
577        eh_frame: Option<D>,
578        stubs_svma: Option<Range<u64>>,
579        stub_helper_svma: Option<Range<u64>>,
580        base_addresses: crate::dwarf::BaseAddresses,
581        text_data: Option<TextByteData<D>>,
582    },
583    /// Used with ELF binaries (Linux and friends), in the `.eh_frame_hdr` and `.eh_frame`
584    /// sections. Contains an index and DWARF CFI.
585    EhFrameHdrAndEhFrame {
586        eh_frame_hdr: D,
587        eh_frame: D,
588        base_addresses: crate::dwarf::BaseAddresses,
589    },
590    /// Used with ELF binaries (Linux and friends), in the `.eh_frame` section. Contains
591    /// DWARF CFI. We create a binary index for the FDEs when a module with this unwind
592    /// data type is added.
593    DwarfCfiIndexAndEhFrame {
594        index: DwarfCfiIndex,
595        eh_frame: D,
596        base_addresses: crate::dwarf::BaseAddresses,
597    },
598    /// Used with ELF binaries (Linux and friends), in the `.debug_frame` section. Contains
599    /// DWARF CFI. We create a binary index for the FDEs when a module with this unwind
600    /// data type is added.
601    DwarfCfiIndexAndDebugFrame {
602        index: DwarfCfiIndex,
603        debug_frame: D,
604        base_addresses: crate::dwarf::BaseAddresses,
605    },
606    /// Used with PE binaries (Windows).
607    #[cfg(feature = "pe")]
608    PeUnwindInfo {
609        pdata: D,
610        rdata: Option<DataAtRvaRange<D>>,
611        xdata: Option<DataAtRvaRange<D>>,
612        text: Option<DataAtRvaRange<D>>,
613    },
614    /// No unwind information is used. Unwinding in this module will use a fallback rule
615    /// (usually frame pointer unwinding).
616    None,
617}
618
619impl<D: Deref<Target = [u8]>> ModuleUnwindDataInternal<D> {
620    fn new(section_info: &mut impl ModuleSectionInfo<D>) -> Self {
621        use crate::dwarf::base_addresses_for_sections;
622
623        #[cfg(feature = "macho")]
624        if let Some(unwind_info) = section_info.section_data(b"__unwind_info") {
625            let eh_frame = section_info.section_data(b"__eh_frame");
626            let stubs = section_info.section_svma_range(b"__stubs");
627            let stub_helper = section_info.section_svma_range(b"__stub_helper");
628            // Get the bytes of the executable code (instructions).
629            //
630            // In mach-O objects, executable code is stored in the `__TEXT` segment, which contains
631            // multiple executable sections such as `__text`, `__stubs`, and `__stub_helper`. If we
632            // don't have the full `__TEXT` segment contents, we can fall back to the contents of
633            // just the `__text` section.
634            let text_data = if let (Some(bytes), Some(svma_range)) = (
635                section_info.segment_data(b"__TEXT"),
636                section_info.segment_svma_range(b"__TEXT"),
637            ) {
638                Some(TextByteData { bytes, svma_range })
639            } else if let (Some(bytes), Some(svma_range)) = (
640                section_info.section_data(b"__text"),
641                section_info.section_svma_range(b"__text"),
642            ) {
643                Some(TextByteData { bytes, svma_range })
644            } else {
645                None
646            };
647            return ModuleUnwindDataInternal::CompactUnwindInfoAndEhFrame {
648                unwind_info,
649                eh_frame,
650                stubs_svma: stubs,
651                stub_helper_svma: stub_helper,
652                base_addresses: base_addresses_for_sections(section_info),
653                text_data,
654            };
655        }
656
657        #[cfg(feature = "pe")]
658        if let Some(pdata) = section_info.section_data(b".pdata") {
659            let mut range_and_data = |name| {
660                let rva_range = section_info.section_svma_range(name).and_then(|range| {
661                    Some(Range {
662                        start: (range.start - section_info.base_svma()).try_into().ok()?,
663                        end: (range.end - section_info.base_svma()).try_into().ok()?,
664                    })
665                })?;
666                let data = section_info.section_data(name)?;
667                Some(DataAtRvaRange { data, rva_range })
668            };
669            return ModuleUnwindDataInternal::PeUnwindInfo {
670                pdata,
671                rdata: range_and_data(b".rdata"),
672                xdata: range_and_data(b".xdata"),
673                text: range_and_data(b".text"),
674            };
675        }
676
677        if let Some(eh_frame) = section_info
678            .section_data(b".eh_frame")
679            .or_else(|| section_info.section_data(b"__eh_frame"))
680        {
681            if let Some(eh_frame_hdr) = section_info
682                .section_data(b".eh_frame_hdr")
683                .or_else(|| section_info.section_data(b"__eh_frame_hdr"))
684            {
685                ModuleUnwindDataInternal::EhFrameHdrAndEhFrame {
686                    eh_frame_hdr,
687                    eh_frame,
688                    base_addresses: base_addresses_for_sections(section_info),
689                }
690            } else {
691                match DwarfCfiIndex::try_new_eh_frame(&eh_frame, section_info) {
692                    Ok(index) => ModuleUnwindDataInternal::DwarfCfiIndexAndEhFrame {
693                        index,
694                        eh_frame,
695                        base_addresses: base_addresses_for_sections(section_info),
696                    },
697                    Err(_) => ModuleUnwindDataInternal::None,
698                }
699            }
700        } else if let Some(debug_frame) = section_info.section_data(b".debug_frame") {
701            match DwarfCfiIndex::try_new_debug_frame(&debug_frame, section_info) {
702                Ok(index) => ModuleUnwindDataInternal::DwarfCfiIndexAndDebugFrame {
703                    index,
704                    debug_frame,
705                    base_addresses: base_addresses_for_sections(section_info),
706                },
707                Err(_) => ModuleUnwindDataInternal::None,
708            }
709        } else {
710            ModuleUnwindDataInternal::None
711        }
712    }
713}
714
715/// Used to supply raw instruction bytes to the unwinder, which uses it to analyze
716/// instructions in order to provide high quality unwinding inside function prologues and
717/// epilogues.
718///
719/// This is only needed on macOS, because mach-O `__unwind_info` and `__eh_frame` only
720/// cares about accuracy in function bodies, not in function prologues and epilogues.
721///
722/// On Linux, compilers produce `.eh_frame` and `.debug_frame` which provides correct
723/// unwind information for all instructions including those in function prologues and
724/// epilogues, so instruction analysis is not needed.
725///
726/// Type arguments:
727///
728///  - `D`: The type for unwind section data. This allows carrying owned data on the
729///    module, e.g. `Vec<u8>`. But it could also be a wrapper around mapped memory from
730///    a file or a different process, for example. It just needs to provide a slice of
731///    bytes via its `Deref` implementation.
732#[cfg(feature = "macho")]
733struct TextByteData<D> {
734    pub bytes: D,
735    pub svma_range: Range<u64>,
736}
737
738/// Information about a module that is loaded in a process. You might know this under a
739/// different name, for example: (Shared) library, binary image, DSO ("Dynamic shared object")
740///
741/// The unwinder needs to have an up-to-date list of modules so that it can match an
742/// absolute address to the right module, and so that it can find that module's unwind
743/// information.
744///
745/// Type arguments:
746///
747///  - `D`: The type for unwind section data. This allows carrying owned data on the
748///    module, e.g. `Vec<u8>`. But it could also be a wrapper around mapped memory from
749///    a file or a different process, for example. It just needs to provide a slice of
750///    bytes via its `Deref` implementation.
751pub struct Module<D> {
752    /// The name or file path of the module. Unused, it's just there for easier debugging.
753    #[allow(unused)]
754    name: String,
755    /// The address range where this module is mapped into the process.
756    avma_range: Range<u64>,
757    /// The base address of this module, in the process's address space. On Linux, the base
758    /// address can sometimes be different from the start address of the mapped range.
759    base_avma: u64,
760    /// The base address of this module, according to the module.
761    base_svma: u64,
762    /// The unwind data that should be used for unwinding addresses from this module.
763    unwind_data: Arc<ModuleUnwindDataInternal<D>>,
764}
765
766impl<D> Clone for Module<D> {
767    fn clone(&self) -> Self {
768        Self {
769            name: self.name.clone(),
770            avma_range: self.avma_range.clone(),
771            base_avma: self.base_avma,
772            base_svma: self.base_svma,
773            unwind_data: self.unwind_data.clone(),
774        }
775    }
776}
777
778/// Information about a module's sections (and segments).
779///
780/// This trait is used as an interface to module information, and each function with `&mut self` is
781/// called at most once with a particular argument (e.g., `section_data(b".text")` will be called
782/// at most once, so it can move data out of the underlying type if desired).
783///
784/// Type arguments:
785///
786///  - `D`: The type for section data. This allows carrying owned data on the module, e.g.
787///    `Vec<u8>`. But it could also be a wrapper around mapped memory from a file or a different
788///    process, for example.
789pub trait ModuleSectionInfo<D> {
790    /// Return the base address stated in the module.
791    ///
792    /// For mach-O objects, this is the vmaddr of the __TEXT segment. For ELF objects, this is
793    /// zero. For PE objects, this is the image base address.
794    ///
795    /// This is used to convert between SVMAs and relative addresses.
796    fn base_svma(&self) -> u64;
797
798    /// Get the given section's memory range, as stated in the module.
799    fn section_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>>;
800
801    /// Get the given section's data. This will only be called once per section.
802    fn section_data(&mut self, name: &[u8]) -> Option<D>;
803
804    /// Get the given segment's memory range, as stated in the module.
805    fn segment_svma_range(&mut self, _name: &[u8]) -> Option<Range<u64>> {
806        None
807    }
808
809    /// Get the given segment's data. This will only be called once per segment.
810    fn segment_data(&mut self, _name: &[u8]) -> Option<D> {
811        None
812    }
813}
814
815/// Explicit addresses and data of various sections in the module. This implements
816/// the `ModuleSectionInfo` trait.
817///
818/// Unless otherwise stated, these are SVMAs, "stated virtual memory addresses", i.e. addresses as
819/// stated in the object, as opposed to AVMAs, "actual virtual memory addresses", i.e. addresses in
820/// the virtual memory of the profiled process.
821///
822/// Code addresses inside a module's unwind information are usually written down as SVMAs,
823/// or as relative addresses. For example, DWARF CFI can have code addresses expressed as
824/// relative-to-.text addresses or as absolute SVMAs. And mach-O compact unwind info
825/// contains addresses relative to the image base address.
826#[derive(Clone, Debug, Default, PartialEq, Eq)]
827pub struct ExplicitModuleSectionInfo<D> {
828    /// The image base address, as stated in the object. For mach-O objects, this is the
829    /// vmaddr of the `__TEXT` segment. For ELF objects, this is zero.
830    ///
831    /// This is used to convert between SVMAs and relative addresses.
832    pub base_svma: u64,
833    /// The address range of the `__text` or `.text` section. This is where most of the compiled
834    /// code is stored.
835    ///
836    /// This is used to detect whether we need to do instruction analysis for an address.
837    pub text_svma: Option<Range<u64>>,
838    /// The data of the `__text` or `.text` section. This is where most of the compiled code is
839    /// stored. For mach-O binaries, this does not need to be supplied if `text_segment` is supplied.
840    ///
841    /// This is used to handle function prologues and epilogues in some cases.
842    pub text: Option<D>,
843    /// The address range of the mach-O `__stubs` section. Contains small pieces of
844    /// executable code for calling imported functions. Code inside this section is not
845    /// covered by the unwind information in `__unwind_info`.
846    ///
847    /// This is used to exclude addresses in this section from incorrectly applying
848    /// `__unwind_info` opcodes. It is also used to infer unwind rules for the known
849    /// structure of stub functions.
850    pub stubs_svma: Option<Range<u64>>,
851    /// The address range of the mach-O `__stub_helper` section. Contains small pieces of
852    /// executable code for calling imported functions. Code inside this section is not
853    /// covered by the unwind information in `__unwind_info`.
854    ///
855    /// This is used to exclude addresses in this section from incorrectly applying
856    /// `__unwind_info` opcodes. It is also used to infer unwind rules for the known
857    /// structure of stub helper
858    /// functions.
859    pub stub_helper_svma: Option<Range<u64>>,
860    /// The address range of the `.got` section (Global Offset Table). This is used
861    /// during DWARF CFI processing, to resolve got-relative addresses.
862    pub got_svma: Option<Range<u64>>,
863    /// The data of the `__unwind_info` section of mach-O binaries.
864    pub unwind_info: Option<D>,
865    /// The address range of the `__eh_frame` or `.eh_frame` section. This is used during DWARF CFI
866    /// processing, to resolve eh_frame-relative addresses.
867    pub eh_frame_svma: Option<Range<u64>>,
868    /// The data of the `__eh_frame` or `.eh_frame` section. This is used during DWARF CFI
869    /// processing, to resolve eh_frame-relative addresses.
870    pub eh_frame: Option<D>,
871    /// The address range of the `.eh_frame_hdr` section. This is used during DWARF CFI processing,
872    /// to resolve eh_frame_hdr-relative addresses.
873    pub eh_frame_hdr_svma: Option<Range<u64>>,
874    /// The data of the `.eh_frame_hdr` section. This is used during DWARF CFI processing, to
875    /// resolve eh_frame_hdr-relative addresses.
876    pub eh_frame_hdr: Option<D>,
877    /// The data of the `.debug_frame` section. The related address range is not needed.
878    pub debug_frame: Option<D>,
879    /// The address range of the `__TEXT` segment of mach-O binaries, if available.
880    pub text_segment_svma: Option<Range<u64>>,
881    /// The data of the `__TEXT` segment of mach-O binaries, if available.
882    pub text_segment: Option<D>,
883}
884
885impl<D> ModuleSectionInfo<D> for ExplicitModuleSectionInfo<D>
886where
887    D: Deref<Target = [u8]>,
888{
889    fn base_svma(&self) -> u64 {
890        self.base_svma
891    }
892
893    fn section_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
894        match name {
895            b"__text" | b".text" => self.text_svma.clone(),
896            b"__stubs" => self.stubs_svma.clone(),
897            b"__stub_helper" => self.stub_helper_svma.clone(),
898            b"__eh_frame" | b".eh_frame" => self.eh_frame_svma.clone(),
899            b"__eh_frame_hdr" | b".eh_frame_hdr" => self.eh_frame_hdr_svma.clone(),
900            b"__got" | b".got" => self.got_svma.clone(),
901            _ => None,
902        }
903    }
904    fn section_data(&mut self, name: &[u8]) -> Option<D> {
905        match name {
906            b"__text" | b".text" => self.text.take(),
907            b"__unwind_info" => self.unwind_info.take(),
908            b"__eh_frame" | b".eh_frame" => self.eh_frame.take(),
909            b"__eh_frame_hdr" | b".eh_frame_hdr" => self.eh_frame_hdr.take(),
910            b"__debug_frame" | b".debug_frame" => self.debug_frame.take(),
911            _ => None,
912        }
913    }
914    fn segment_svma_range(&mut self, name: &[u8]) -> Option<Range<u64>> {
915        match name {
916            b"__TEXT" => self.text_segment_svma.clone(),
917            _ => None,
918        }
919    }
920    fn segment_data(&mut self, name: &[u8]) -> Option<D> {
921        match name {
922            b"__TEXT" => self.text_segment.take(),
923            _ => None,
924        }
925    }
926}
927
928impl<D: Deref<Target = [u8]>> Module<D> {
929    pub fn new(
930        name: String,
931        avma_range: core::ops::Range<u64>,
932        base_avma: u64,
933        mut section_info: impl ModuleSectionInfo<D>,
934    ) -> Self {
935        let unwind_data = ModuleUnwindDataInternal::new(&mut section_info);
936
937        Self {
938            name,
939            avma_range,
940            base_avma,
941            base_svma: section_info.base_svma(),
942            unwind_data: Arc::new(unwind_data),
943        }
944    }
945
946    pub fn avma_range(&self) -> core::ops::Range<u64> {
947        self.avma_range.clone()
948    }
949
950    pub fn base_avma(&self) -> u64 {
951        self.base_avma
952    }
953
954    pub fn name(&self) -> &str {
955        &self.name
956    }
957}