use core::marker::PhantomData; use core::ops::{Deref, Range}; use core::sync::atomic::{AtomicU16, Ordering};
/// Unwinder is the trait that each CPU architecture's concrete unwinder type implements. /// This trait's methods are what let you do the actual unwinding. pubtrait Unwinder: Clone { /// The unwind registers type for the targeted CPU architecture. type UnwindRegs;
/// The unwind cache for the targeted CPU architecture. /// This is an associated type because the cache stores unwind rules, whose concrete /// type depends on the CPU arch, and because the cache can support different allocation /// policies. type Cache;
/// The module type. This is an associated type because the concrete type varies /// depending on the type you use to give the module access to the unwind section data. type Module;
/// Add a module that's loaded in the profiled process. This is how you provide unwind /// information and address ranges. /// /// This should be called whenever a new module is loaded into the process. fn add_module(&mutself, module: Self::Module);
/// Remove a module that was added before using `add_module`, keyed by the start /// address of that module's address range. If no match is found, the call is ignored. /// This should be called whenever a module is unloaded from the process. fn remove_module(&mutself, module_avma_range_start: u64);
/// Returns the highest code address that is known in this process based on the module /// address ranges. Returns 0 if no modules have been added. /// /// This method can be used together with /// [`PtrAuthMask::from_max_known_address`](crate::aarch64::PtrAuthMask::from_max_known_address) /// to make an educated guess at a pointer authentication mask for Aarch64 return addresses. fn max_known_code_address(&self) -> u64;
/// Unwind a single frame, to recover return address and caller register values. /// This is the main entry point for unwinding. fn unwind_frame<F>(
&self,
address: FrameAddress,
regs: &mutSelf::UnwindRegs,
cache: &mutSelf::Cache,
read_stack: &mut F,
) -> Result<Option<u64>, Error> where
F: FnMut(u64) -> Result<u64, ()>;
/// Return an iterator that unwinds frame by frame until the end of the stack is found. fn iter_frames<'u, 'c, 'r, F>(
&'u self,
pc: u64,
regs: Self::UnwindRegs,
cache: &'c mut Self::Cache,
read_stack: &'r mut F,
) -> UnwindIterator<'u, 'c, 'r, Self, F> where
F: FnMut(u64) -> Result<u64, ()>,
{
UnwindIterator::new(self, pc, regs, cache, read_stack)
}
}
/// An iterator for unwinding the entire stack, starting from the initial register values. /// /// The first yielded frame is the instruction pointer. Subsequent addresses are return /// addresses. /// /// This iterator attempts to detect if stack unwinding completed successfully, or if the /// stack was truncated prematurely. If it thinks that it successfully found the root /// function, it will complete with `Ok(None)`, otherwise it will complete with `Err(...)`. /// However, the detection does not work in all cases, so you should expect `Err(...)` to /// be returned even during normal operation. As a result, it is not recommended to use /// this iterator as a `FallibleIterator`, because you might lose the entire stack if the /// last iteration returns `Err(...)`. /// /// Lifetimes: /// /// - `'u`: The lifetime of the [`Unwinder`]. /// - `'c`: The lifetime of the unwinder cache. /// - `'r`: The lifetime of the exclusive access to the `read_stack` callback. pubstruct UnwindIterator<'u, 'c, 'r, U: Unwinder + ?Sized, F: FnMut(u64) -> Result<u64, ()>> {
unwinder: &'u U,
state: UnwindIteratorState,
regs: U::UnwindRegs,
cache: &'c mut U::Cache,
read_stack: &'r mut F,
}
impl<'u, 'c, 'r, U: Unwinder + ?Sized, F: FnMut(u64) -> Result<u64, ()>>
UnwindIterator<'u, 'c, 'r, U, F>
{ /// Create a new iterator. You'd usually use [`Unwinder::iter_frames`] instead. pubfn new(
unwinder: &'u U,
pc: u64,
regs: U::UnwindRegs,
cache: &'c mut U::Cache,
read_stack: &'r mut F,
) -> Self { Self {
unwinder,
state: UnwindIteratorState::Initial(pc),
regs,
cache,
read_stack,
}
}
}
impl<'u, 'c, 'r, U: Unwinder + ?Sized, F: FnMut(u64) -> Result<u64, ()>>
UnwindIterator<'u, 'c, 'r, U, F>
{ /// Yield the next frame in the stack. /// /// The first frame is `Ok(Some(FrameAddress::InstructionPointer(...)))`. /// Subsequent frames are `Ok(Some(FrameAddress::ReturnAddress(...)))`. /// /// If a root function has been reached, this iterator completes with `Ok(None)`. /// Otherwise it completes with `Err(...)`, usually indicating that a certain stack /// address could not be read. #[allow(clippy::should_implement_trait)] pubfn next(&mutself) -> Result<Option<FrameAddress>, Error> { let next = matchself.state {
UnwindIteratorState::Initial(pc) => { self.state = UnwindIteratorState::Unwinding(FrameAddress::InstructionPointer(pc)); return Ok(Some(FrameAddress::InstructionPointer(pc)));
}
UnwindIteratorState::Unwinding(address) => { self.unwinder
.unwind_frame(address, &mutself.regs, self.cache, self.read_stack)?
}
UnwindIteratorState::Done => return Ok(None),
}; match next {
Some(return_address) => { let return_address = FrameAddress::from_return_address(return_address)
.ok_or(Error::ReturnAddressIsNull)?; self.state = UnwindIteratorState::Unwinding(return_address);
Ok(Some(return_address))
}
None => { self.state = UnwindIteratorState::Done;
Ok(None)
}
}
}
}
impl<'u, 'c, 'r, U: Unwinder + ?Sized, F: FnMut(u64) -> Result<u64, ()>> FallibleIterator for UnwindIterator<'u, 'c, 'r, U, F>
{ type Item = FrameAddress; type Error = Error;
/// This global generation counter makes it so that the cache can be shared /// between multiple unwinders. /// This is a u16, so if you make it wrap around by adding / removing modules /// more than 65535 times, then you risk collisions in the cache; meaning: /// unwinding might not work properly if an old unwind rule was found in the /// cache for the same address and the same (pre-wraparound) modules_generation. static GLOBAL_MODULES_GENERATION: AtomicU16 = AtomicU16::new(0);
pubstruct UnwinderInternal<D, A, P> { /// sorted by avma_range.start
modules: Vec<Module<D>>, /// Incremented every time modules is changed.
modules_generation: u16,
_arch: PhantomData<A>,
_allocation_policy: PhantomData<P>,
}
impl<D, A, P> Default for UnwinderInternal<D, A, P> { fn default() -> Self { Self::new()
}
}
impl<D, A, P> Clone for UnwinderInternal<D, A, P> { fn clone(&self) -> Self { Self {
modules: self.modules.clone(),
modules_generation: self.modules_generation,
_arch: PhantomData,
_allocation_policy: PhantomData,
}
}
}
/// The unwind data that should be used when unwinding addresses inside this module. /// Unwind data describes how to recover register values of the caller frame. /// /// The type of unwind information you use depends on the platform and what's available /// in the binary. /// /// Type arguments: /// /// - `D`: The type for unwind section data. This allows carrying owned data on the /// module, e.g. `Vec<u8>`. But it could also be a wrapper around mapped memory from /// a file or a different process, for example. It just needs to provide a slice of /// bytes via its `Deref` implementation. enum ModuleUnwindDataInternal<D> { /// Used on macOS, with mach-O binaries. Compact unwind info is in the `__unwind_info` /// section and is sometimes supplemented with DWARF CFI information in the `__eh_frame` /// section. `__stubs` and `__stub_helper` ranges are used by the unwinder. #[cfg(feature = "macho")]
CompactUnwindInfoAndEhFrame {
unwind_info: D,
eh_frame: Option<D>,
stubs_svma: Option<Range<u64>>,
stub_helper_svma: Option<Range<u64>>,
base_addresses: crate::dwarf::BaseAddresses,
text_data: Option<TextByteData<D>>,
}, /// Used with ELF binaries (Linux and friends), in the `.eh_frame_hdr` and `.eh_frame` /// sections. Contains an index and DWARF CFI.
EhFrameHdrAndEhFrame {
eh_frame_hdr: D,
eh_frame: D,
base_addresses: crate::dwarf::BaseAddresses,
}, /// Used with ELF binaries (Linux and friends), in the `.eh_frame` section. Contains /// DWARF CFI. We create a binary index for the FDEs when a module with this unwind /// data type is added.
DwarfCfiIndexAndEhFrame {
index: DwarfCfiIndex,
eh_frame: D,
base_addresses: crate::dwarf::BaseAddresses,
}, /// Used with ELF binaries (Linux and friends), in the `.debug_frame` section. Contains /// DWARF CFI. We create a binary index for the FDEs when a module with this unwind /// data type is added.
DwarfCfiIndexAndDebugFrame {
index: DwarfCfiIndex,
debug_frame: D,
base_addresses: crate::dwarf::BaseAddresses,
}, /// Used with PE binaries (Windows). #[cfg(feature = "pe")]
PeUnwindInfo {
pdata: D,
rdata: Option<DataAtRvaRange<D>>,
xdata: Option<DataAtRvaRange<D>>,
text: Option<DataAtRvaRange<D>>,
}, /// No unwind information is used. Unwinding in this module will use a fallback rule /// (usually frame pointer unwinding).
None,
}
#[cfg(feature = "macho")] iflet Some(unwind_info) = section_info.section_data(b"__unwind_info") { let eh_frame = section_info.section_data(b"__eh_frame"); let stubs = section_info.section_svma_range(b"__stubs"); let stub_helper = section_info.section_svma_range(b"__stub_helper"); // Get the bytes of the executable code (instructions). // // In mach-O objects, executable code is stored in the `__TEXT` segment, which contains // multiple executable sections such as `__text`, `__stubs`, and `__stub_helper`. If we // don't have the full `__TEXT` segment contents, we can fall back to the contents of // just the `__text` section. let text_data = iflet (Some(bytes), Some(svma_range)) = (
section_info.segment_data(b"__TEXT"),
section_info.segment_svma_range(b"__TEXT"),
) {
Some(TextByteData { bytes, svma_range })
} elseiflet (Some(bytes), Some(svma_range)) = (
section_info.section_data(b"__text"),
section_info.section_svma_range(b"__text"),
) {
Some(TextByteData { bytes, svma_range })
} else {
None
}; return ModuleUnwindDataInternal::CompactUnwindInfoAndEhFrame {
unwind_info,
eh_frame,
stubs_svma: stubs,
stub_helper_svma: stub_helper,
base_addresses: base_addresses_for_sections(section_info),
text_data,
};
}
/// Used to supply raw instruction bytes to the unwinder, which uses it to analyze /// instructions in order to provide high quality unwinding inside function prologues and /// epilogues. /// /// This is only needed on macOS, because mach-O `__unwind_info` and `__eh_frame` only /// cares about accuracy in function bodies, not in function prologues and epilogues. /// /// On Linux, compilers produce `.eh_frame` and `.debug_frame` which provides correct /// unwind information for all instructions including those in function prologues and /// epilogues, so instruction analysis is not needed. /// /// Type arguments: /// /// - `D`: The type for unwind section data. This allows carrying owned data on the /// module, e.g. `Vec<u8>`. But it could also be a wrapper around mapped memory from /// a file or a different process, for example. It just needs to provide a slice of /// bytes via its `Deref` implementation. #[cfg(feature = "macho")] struct TextByteData<D> { pub bytes: D, pub svma_range: Range<u64>,
}
/// Information about a module that is loaded in a process. You might know this under a /// different name, for example: (Shared) library, binary image, DSO ("Dynamic shared object") /// /// The unwinder needs to have an up-to-date list of modules so that it can match an /// absolute address to the right module, and so that it can find that module's unwind /// information. /// /// Type arguments: /// /// - `D`: The type for unwind section data. This allows carrying owned data on the /// module, e.g. `Vec<u8>`. But it could also be a wrapper around mapped memory from /// a file or a different process, for example. It just needs to provide a slice of /// bytes via its `Deref` implementation. pubstruct Module<D> { /// The name or file path of the module. Unused, it's just there for easier debugging. #[allow(unused)]
name: String, /// The address range where this module is mapped into the process.
avma_range: Range<u64>, /// The base address of this module, in the process's address space. On Linux, the base /// address can sometimes be different from the start address of the mapped range.
base_avma: u64, /// The base address of this module, according to the module.
base_svma: u64, /// The unwind data that should be used for unwinding addresses from this module.
unwind_data: Arc<ModuleUnwindDataInternal<D>>,
}
/// Information about a module's sections (and segments). /// /// This trait is used as an interface to module information, and each function with `&mut self` is /// called at most once with a particular argument (e.g., `section_data(b".text")` will be called /// at most once, so it can move data out of the underlying type if desired). /// /// Type arguments: /// /// - `D`: The type for section data. This allows carrying owned data on the module, e.g. /// `Vec<u8>`. But it could also be a wrapper around mapped memory from a file or a different /// process, for example. pubtrait ModuleSectionInfo<D> { /// Return the base address stated in the module. /// /// For mach-O objects, this is the vmaddr of the __TEXT segment. For ELF objects, this is /// zero. For PE objects, this is the image base address. /// /// This is used to convert between SVMAs and relative addresses. fn base_svma(&self) -> u64;
/// Get the given section's memory range, as stated in the module. fn section_svma_range(&mutself, name: &[u8]) -> Option<Range<u64>>;
/// Get the given section's data. This will only be called once per section. fn section_data(&mutself, name: &[u8]) -> Option<D>;
/// Get the given segment's memory range, as stated in the module. fn segment_svma_range(&mutself, _name: &[u8]) -> Option<Range<u64>> {
None
}
/// Get the given segment's data. This will only be called once per segment. fn segment_data(&mutself, _name: &[u8]) -> Option<D> {
None
}
}
/// Explicit addresses and data of various sections in the module. This implements /// the `ModuleSectionInfo` trait. /// /// Unless otherwise stated, these are SVMAs, "stated virtual memory addresses", i.e. addresses as /// stated in the object, as opposed to AVMAs, "actual virtual memory addresses", i.e. addresses in /// the virtual memory of the profiled process. /// /// Code addresses inside a module's unwind information are usually written down as SVMAs, /// or as relative addresses. For example, DWARF CFI can have code addresses expressed as /// relative-to-.text addresses or as absolute SVMAs. And mach-O compact unwind info /// contains addresses relative to the image base address. #[derive(Clone, Debug, Default, PartialEq, Eq)] pubstruct ExplicitModuleSectionInfo<D> { /// The image base address, as stated in the object. For mach-O objects, this is the /// vmaddr of the `__TEXT` segment. For ELF objects, this is zero. /// /// This is used to convert between SVMAs and relative addresses. pub base_svma: u64, /// The address range of the `__text` or `.text` section. This is where most of the compiled /// code is stored. /// /// This is used to detect whether we need to do instruction analysis for an address. pub text_svma: Option<Range<u64>>, /// The data of the `__text` or `.text` section. This is where most of the compiled code is /// stored. For mach-O binaries, this does not need to be supplied if `text_segment` is supplied. /// /// This is used to handle function prologues and epilogues in some cases. pub text: Option<D>, /// The address range of the mach-O `__stubs` section. Contains small pieces of /// executable code for calling imported functions. Code inside this section is not /// covered by the unwind information in `__unwind_info`. /// /// This is used to exclude addresses in this section from incorrectly applying /// `__unwind_info` opcodes. It is also used to infer unwind rules for the known /// structure of stub functions. pub stubs_svma: Option<Range<u64>>, /// The address range of the mach-O `__stub_helper` section. Contains small pieces of /// executable code for calling imported functions. Code inside this section is not /// covered by the unwind information in `__unwind_info`. /// /// This is used to exclude addresses in this section from incorrectly applying /// `__unwind_info` opcodes. It is also used to infer unwind rules for the known /// structure of stub helper /// functions. pub stub_helper_svma: Option<Range<u64>>, /// The address range of the `.got` section (Global Offset Table). This is used /// during DWARF CFI processing, to resolve got-relative addresses. pub got_svma: Option<Range<u64>>, /// The data of the `__unwind_info` section of mach-O binaries. pub unwind_info: Option<D>, /// The address range of the `__eh_frame` or `.eh_frame` section. This is used during DWARF CFI /// processing, to resolve eh_frame-relative addresses. pub eh_frame_svma: Option<Range<u64>>, /// The data of the `__eh_frame` or `.eh_frame` section. This is used during DWARF CFI /// processing, to resolve eh_frame-relative addresses. pub eh_frame: Option<D>, /// The address range of the `.eh_frame_hdr` section. This is used during DWARF CFI processing, /// to resolve eh_frame_hdr-relative addresses. pub eh_frame_hdr_svma: Option<Range<u64>>, /// The data of the `.eh_frame_hdr` section. This is used during DWARF CFI processing, to /// resolve eh_frame_hdr-relative addresses. pub eh_frame_hdr: Option<D>, /// The data of the `.debug_frame` section. The related address range is not needed. pub debug_frame: Option<D>, /// The address range of the `__TEXT` segment of mach-O binaries, if available. pub text_segment_svma: Option<Range<u64>>, /// The data of the `__TEXT` segment of mach-O binaries, if available. pub text_segment: Option<D>,
}
impl<D> ModuleSectionInfo<D> for ExplicitModuleSectionInfo<D> where
D: Deref<Target = [u8]>,
{ fn base_svma(&self) -> u64 { self.base_svma
}
#[cfg(feature = "object")] mod object { usesuper::{ModuleSectionInfo, Range}; use object::read::{Object, ObjectSection, ObjectSegment};
impl<'data: 'file, 'file, O, D> ModuleSectionInfo<D> for &'file O where
O: Object<'data>,
D: From<&'data [u8]>,
{ fn base_svma(&self) -> u64 { iflet Some(text_segment) = self.segments().find(|s| s.name() == Ok(Some("__TEXT"))) { // This is a mach-O image. "Relative addresses" are relative to the // vmaddr of the __TEXT segment. return text_segment.address();
}
// For PE binaries, relative_address_base() returns the image base address. // Otherwise it returns zero. This gives regular ELF images a base address of zero, // which is what we want. self.relative_address_base()
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.