summaryrefslogtreecommitdiffstats
path: root/library/backtrace/src/print
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /library/backtrace/src/print
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--library/backtrace/src/print.rs302
-rw-r--r--library/backtrace/src/print/fuchsia.rs436
2 files changed, 738 insertions, 0 deletions
diff --git a/library/backtrace/src/print.rs b/library/backtrace/src/print.rs
new file mode 100644
index 000000000..cc677122a
--- /dev/null
+++ b/library/backtrace/src/print.rs
@@ -0,0 +1,302 @@
+#[cfg(feature = "std")]
+use super::{BacktraceFrame, BacktraceSymbol};
+use super::{BytesOrWideString, Frame, SymbolName};
+use core::ffi::c_void;
+use core::fmt;
+
+const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::<usize>();
+
+#[cfg(target_os = "fuchsia")]
+mod fuchsia;
+
+/// A formatter for backtraces.
+///
+/// This type can be used to print a backtrace regardless of where the backtrace
+/// itself comes from. If you have a `Backtrace` type then its `Debug`
+/// implementation already uses this printing format.
+pub struct BacktraceFmt<'a, 'b> {
+ fmt: &'a mut fmt::Formatter<'b>,
+ frame_index: usize,
+ format: PrintFmt,
+ print_path:
+ &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result + 'b),
+}
+
+/// The styles of printing that we can print
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum PrintFmt {
+ /// Prints a terser backtrace which ideally only contains relevant information
+ Short,
+ /// Prints a backtrace that contains all possible information
+ Full,
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+impl<'a, 'b> BacktraceFmt<'a, 'b> {
+ /// Create a new `BacktraceFmt` which will write output to the provided
+ /// `fmt`.
+ ///
+ /// The `format` argument will control the style in which the backtrace is
+ /// printed, and the `print_path` argument will be used to print the
+ /// `BytesOrWideString` instances of filenames. This type itself doesn't do
+ /// any printing of filenames, but this callback is required to do so.
+ pub fn new(
+ fmt: &'a mut fmt::Formatter<'b>,
+ format: PrintFmt,
+ print_path: &'a mut (dyn FnMut(&mut fmt::Formatter<'_>, BytesOrWideString<'_>) -> fmt::Result
+ + 'b),
+ ) -> Self {
+ BacktraceFmt {
+ fmt,
+ frame_index: 0,
+ format,
+ print_path,
+ }
+ }
+
+ /// Prints a preamble for the backtrace about to be printed.
+ ///
+ /// This is required on some platforms for backtraces to be fully
+ /// symbolicated later, and otherwise this should just be the first method
+ /// you call after creating a `BacktraceFmt`.
+ pub fn add_context(&mut self) -> fmt::Result {
+ #[cfg(target_os = "fuchsia")]
+ fuchsia::print_dso_context(self.fmt)?;
+ Ok(())
+ }
+
+ /// Adds a frame to the backtrace output.
+ ///
+ /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used
+ /// to actually print a frame, and on destruction it will increment the
+ /// frame counter.
+ pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> {
+ BacktraceFrameFmt {
+ fmt: self,
+ symbol_index: 0,
+ }
+ }
+
+ /// Completes the backtrace output.
+ ///
+ /// This is currently a no-op but is added for future compatibility with
+ /// backtrace formats.
+ pub fn finish(&mut self) -> fmt::Result {
+ // Currently a no-op-- including this hook to allow for future additions.
+ Ok(())
+ }
+}
+
+/// A formatter for just one frame of a backtrace.
+///
+/// This type is created by the `BacktraceFmt::frame` function.
+pub struct BacktraceFrameFmt<'fmt, 'a, 'b> {
+ fmt: &'fmt mut BacktraceFmt<'a, 'b>,
+ symbol_index: usize,
+}
+
+impl BacktraceFrameFmt<'_, '_, '_> {
+ /// Prints a `BacktraceFrame` with this frame formatter.
+ ///
+ /// This will recursively print all `BacktraceSymbol` instances within the
+ /// `BacktraceFrame`.
+ ///
+ /// # Required features
+ ///
+ /// This function requires the `std` feature of the `backtrace` crate to be
+ /// enabled, and the `std` feature is enabled by default.
+ #[cfg(feature = "std")]
+ pub fn backtrace_frame(&mut self, frame: &BacktraceFrame) -> fmt::Result {
+ let symbols = frame.symbols();
+ for symbol in symbols {
+ self.backtrace_symbol(frame, symbol)?;
+ }
+ if symbols.is_empty() {
+ self.print_raw(frame.ip(), None, None, None)?;
+ }
+ Ok(())
+ }
+
+ /// Prints a `BacktraceSymbol` within a `BacktraceFrame`.
+ ///
+ /// # Required features
+ ///
+ /// This function requires the `std` feature of the `backtrace` crate to be
+ /// enabled, and the `std` feature is enabled by default.
+ #[cfg(feature = "std")]
+ pub fn backtrace_symbol(
+ &mut self,
+ frame: &BacktraceFrame,
+ symbol: &BacktraceSymbol,
+ ) -> fmt::Result {
+ self.print_raw_with_column(
+ frame.ip(),
+ symbol.name(),
+ // TODO: this isn't great that we don't end up printing anything
+ // with non-utf8 filenames. Thankfully almost everything is utf8 so
+ // this shouldn't be too too bad.
+ symbol
+ .filename()
+ .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))),
+ symbol.lineno(),
+ symbol.colno(),
+ )?;
+ Ok(())
+ }
+
+ /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw
+ /// callbacks of this crate.
+ pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result {
+ self.print_raw_with_column(
+ frame.ip(),
+ symbol.name(),
+ symbol.filename_raw(),
+ symbol.lineno(),
+ symbol.colno(),
+ )?;
+ Ok(())
+ }
+
+ /// Adds a raw frame to the backtrace output.
+ ///
+ /// This method, unlike the previous, takes the raw arguments in case
+ /// they're being source from different locations. Note that this may be
+ /// called multiple times for one frame.
+ pub fn print_raw(
+ &mut self,
+ frame_ip: *mut c_void,
+ symbol_name: Option<SymbolName<'_>>,
+ filename: Option<BytesOrWideString<'_>>,
+ lineno: Option<u32>,
+ ) -> fmt::Result {
+ self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None)
+ }
+
+ /// Adds a raw frame to the backtrace output, including column information.
+ ///
+ /// This method, like the previous, takes the raw arguments in case
+ /// they're being source from different locations. Note that this may be
+ /// called multiple times for one frame.
+ pub fn print_raw_with_column(
+ &mut self,
+ frame_ip: *mut c_void,
+ symbol_name: Option<SymbolName<'_>>,
+ filename: Option<BytesOrWideString<'_>>,
+ lineno: Option<u32>,
+ colno: Option<u32>,
+ ) -> fmt::Result {
+ // Fuchsia is unable to symbolize within a process so it has a special
+ // format which can be used to symbolize later. Print that instead of
+ // printing addresses in our own format here.
+ if cfg!(target_os = "fuchsia") {
+ self.print_raw_fuchsia(frame_ip)?;
+ } else {
+ self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?;
+ }
+ self.symbol_index += 1;
+ Ok(())
+ }
+
+ #[allow(unused_mut)]
+ fn print_raw_generic(
+ &mut self,
+ mut frame_ip: *mut c_void,
+ symbol_name: Option<SymbolName<'_>>,
+ filename: Option<BytesOrWideString<'_>>,
+ lineno: Option<u32>,
+ colno: Option<u32>,
+ ) -> fmt::Result {
+ // No need to print "null" frames, it basically just means that the
+ // system backtrace was a bit eager to trace back super far.
+ if let PrintFmt::Short = self.fmt.format {
+ if frame_ip.is_null() {
+ return Ok(());
+ }
+ }
+
+ // To reduce TCB size in Sgx enclave, we do not want to implement symbol
+ // resolution functionality. Rather, we can print the offset of the
+ // address here, which could be later mapped to correct function.
+ #[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))]
+ {
+ let image_base = std::os::fortanix_sgx::mem::image_base();
+ frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _;
+ }
+
+ // Print the index of the frame as well as the optional instruction
+ // pointer of the frame. If we're beyond the first symbol of this frame
+ // though we just print appropriate whitespace.
+ if self.symbol_index == 0 {
+ write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?;
+ if let PrintFmt::Full = self.fmt.format {
+ write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?;
+ }
+ } else {
+ write!(self.fmt.fmt, " ")?;
+ if let PrintFmt::Full = self.fmt.format {
+ write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?;
+ }
+ }
+
+ // Next up write out the symbol name, using the alternate formatting for
+ // more information if we're a full backtrace. Here we also handle
+ // symbols which don't have a name,
+ match (symbol_name, &self.fmt.format) {
+ (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?,
+ (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?,
+ (None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "<unknown>")?,
+ }
+ self.fmt.fmt.write_str("\n")?;
+
+ // And last up, print out the filename/line number if they're available.
+ if let (Some(file), Some(line)) = (filename, lineno) {
+ self.print_fileline(file, line, colno)?;
+ }
+
+ Ok(())
+ }
+
+ fn print_fileline(
+ &mut self,
+ file: BytesOrWideString<'_>,
+ line: u32,
+ colno: Option<u32>,
+ ) -> fmt::Result {
+ // Filename/line are printed on lines under the symbol name, so print
+ // some appropriate whitespace to sort of right-align ourselves.
+ if let PrintFmt::Full = self.fmt.format {
+ write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?;
+ }
+ write!(self.fmt.fmt, " at ")?;
+
+ // Delegate to our internal callback to print the filename and then
+ // print out the line number.
+ (self.fmt.print_path)(self.fmt.fmt, file)?;
+ write!(self.fmt.fmt, ":{}", line)?;
+
+ // Add column number, if available.
+ if let Some(colno) = colno {
+ write!(self.fmt.fmt, ":{}", colno)?;
+ }
+
+ write!(self.fmt.fmt, "\n")?;
+ Ok(())
+ }
+
+ fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result {
+ // We only care about the first symbol of a frame
+ if self.symbol_index == 0 {
+ self.fmt.fmt.write_str("{{{bt:")?;
+ write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?;
+ self.fmt.fmt.write_str("}}}\n")?;
+ }
+ Ok(())
+ }
+}
+
+impl Drop for BacktraceFrameFmt<'_, '_, '_> {
+ fn drop(&mut self) {
+ self.fmt.frame_index += 1;
+ }
+}
diff --git a/library/backtrace/src/print/fuchsia.rs b/library/backtrace/src/print/fuchsia.rs
new file mode 100644
index 000000000..959253acb
--- /dev/null
+++ b/library/backtrace/src/print/fuchsia.rs
@@ -0,0 +1,436 @@
+use core::fmt::{self, Write};
+use core::mem::{size_of, transmute};
+use core::slice::from_raw_parts;
+use libc::c_char;
+
+extern "C" {
+ // dl_iterate_phdr takes a callback that will receive a dl_phdr_info pointer
+ // for every DSO that has been linked into the process. dl_iterate_phdr also
+ // ensures that the dynamic linker is locked from start to finish of the
+ // iteration. If the callback returns a non-zero value the iteration is
+ // terminated early. 'data' will be passed as the third argument to the
+ // callback on each call. 'size' gives the size of the dl_phdr_info.
+ #[allow(improper_ctypes)]
+ fn dl_iterate_phdr(
+ f: extern "C" fn(info: &dl_phdr_info, size: usize, data: &mut DsoPrinter<'_, '_>) -> i32,
+ data: &mut DsoPrinter<'_, '_>,
+ ) -> i32;
+}
+
+// We need to parse out the build ID and some basic program header data
+// which means that we need a bit of stuff from the ELF spec as well.
+
+const PT_LOAD: u32 = 1;
+const PT_NOTE: u32 = 4;
+
+// Now we have to replicate, bit for bit, the structure of the dl_phdr_info
+// type used by fuchsia's current dynamic linker. Chromium also has this ABI
+// boundary as well as crashpad. Eventully we'd like to move these cases to
+// use elf-search but we'd need to provide that in the SDK and that has not
+// yet been done. Thus we (and they) are stuck having to use this method
+// which incurs a tight coupling with the fuchsia libc.
+
+#[allow(non_camel_case_types)]
+#[repr(C)]
+struct dl_phdr_info {
+ addr: *const u8,
+ name: *const c_char,
+ phdr: *const Elf_Phdr,
+ phnum: u16,
+ adds: u64,
+ subs: u64,
+ tls_modid: usize,
+ tls_data: *const u8,
+}
+
+impl dl_phdr_info {
+ fn program_headers(&self) -> PhdrIter<'_> {
+ PhdrIter {
+ phdrs: self.phdr_slice(),
+ base: self.addr,
+ }
+ }
+ // We have no way of knowing of checking if e_phoff and e_phnum are valid.
+ // libc should ensure this for us however so it's safe to form a slice here.
+ fn phdr_slice(&self) -> &[Elf_Phdr] {
+ unsafe { from_raw_parts(self.phdr, self.phnum as usize) }
+ }
+}
+
+struct PhdrIter<'a> {
+ phdrs: &'a [Elf_Phdr],
+ base: *const u8,
+}
+
+impl<'a> Iterator for PhdrIter<'a> {
+ type Item = Phdr<'a>;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.phdrs.split_first().map(|(phdr, new_phdrs)| {
+ self.phdrs = new_phdrs;
+ Phdr {
+ phdr,
+ base: self.base,
+ }
+ })
+ }
+}
+
+// Elf_Phdr represents a 64-bit ELF program header in the endianness of the target
+// architecture.
+#[allow(non_camel_case_types)]
+#[derive(Clone, Debug)]
+#[repr(C)]
+struct Elf_Phdr {
+ p_type: u32,
+ p_flags: u32,
+ p_offset: u64,
+ p_vaddr: u64,
+ p_paddr: u64,
+ p_filesz: u64,
+ p_memsz: u64,
+ p_align: u64,
+}
+
+// Phdr represents a valid ELF program header and its contents.
+struct Phdr<'a> {
+ phdr: &'a Elf_Phdr,
+ base: *const u8,
+}
+
+impl<'a> Phdr<'a> {
+ // We have no way of checking if p_addr or p_memsz are valid. Fuchsia's libc
+ // parses the notes first however so by virtue of being here these headers
+ // must be valid. NoteIter does not require the underlying data to be valid
+ // but it does require the bounds to be valid. We trust that libc has ensured
+ // that this is the case for us here.
+ fn notes(&self) -> NoteIter<'a> {
+ unsafe {
+ NoteIter::new(
+ self.base.add(self.phdr.p_offset as usize),
+ self.phdr.p_memsz as usize,
+ )
+ }
+ }
+}
+
+// The note type for build IDs.
+const NT_GNU_BUILD_ID: u32 = 3;
+
+// Elf_Nhdr represents an ELF note header in the endianness of the target.
+#[allow(non_camel_case_types)]
+#[repr(C)]
+struct Elf_Nhdr {
+ n_namesz: u32,
+ n_descsz: u32,
+ n_type: u32,
+}
+
+// Note represents an ELF note (header + contents). The name is left as a u8
+// slice because it is not always null terminated and rust makes it easy enough
+// to check that the bytes match eitherway.
+struct Note<'a> {
+ name: &'a [u8],
+ desc: &'a [u8],
+ tipe: u32,
+}
+
+// NoteIter lets you safely iterate over a note segment. It terminates as soon
+// as an error occurs or there are no more notes. If you iterate over invalid
+// data it will function as though no notes were found.
+struct NoteIter<'a> {
+ base: &'a [u8],
+ error: bool,
+}
+
+impl<'a> NoteIter<'a> {
+ // It is an invariant of function that the pointer and size given denote a
+ // valid range of bytes that can all be read. The contents of these bytes
+ // can be anything but the range must be valid for this to be safe.
+ unsafe fn new(base: *const u8, size: usize) -> Self {
+ NoteIter {
+ base: from_raw_parts(base, size),
+ error: false,
+ }
+ }
+}
+
+// align_to aligns 'x' to 'to'-byte alignment assuming 'to' is a power of 2.
+// This follows a standard pattern in C/C++ ELF parsing code where
+// (x + to - 1) & -to is used. Rust does not let you negate usize so I use
+// 2's-complement conversion to recreate that.
+fn align_to(x: usize, to: usize) -> usize {
+ (x + to - 1) & (!to + 1)
+}
+
+// take_bytes_align4 consumes num bytes from the slice (if present) and
+// additionally ensures that the final slice is properlly aligned. If an
+// either the number of bytes requested is too large or the slice can't be
+// realigned afterwards due to not enough remaining bytes existing, None is
+// returned and the slice is not modified.
+fn take_bytes_align4<'a>(num: usize, bytes: &mut &'a [u8]) -> Option<&'a [u8]> {
+ if bytes.len() < align_to(num, 4) {
+ return None;
+ }
+ let (out, bytes_new) = bytes.split_at(num);
+ *bytes = &bytes_new[align_to(num, 4) - num..];
+ Some(out)
+}
+
+// This function has no real invariants the caller must uphold other than
+// perhaps that 'bytes' should be aligned for performance (and on some
+// architectures correctness). The values in the Elf_Nhdr fields might
+// be nonsense but this function ensures no such thing.
+fn take_nhdr<'a>(bytes: &mut &'a [u8]) -> Option<&'a Elf_Nhdr> {
+ if size_of::<Elf_Nhdr>() > bytes.len() {
+ return None;
+ }
+ // This is safe as long as there is enough space and we just confirmed that
+ // in the if statement above so this should not be unsafe.
+ let out = unsafe { transmute::<*const u8, &'a Elf_Nhdr>(bytes.as_ptr()) };
+ // Note that sice_of::<Elf_Nhdr>() is always 4-byte aligned.
+ *bytes = &bytes[size_of::<Elf_Nhdr>()..];
+ Some(out)
+}
+
+impl<'a> Iterator for NoteIter<'a> {
+ type Item = Note<'a>;
+ fn next(&mut self) -> Option<Self::Item> {
+ // Check if we've reached the end.
+ if self.base.len() == 0 || self.error {
+ return None;
+ }
+ // We transmute out an nhdr but we carefully consider the resulting
+ // struct. We don't trust the namesz or descsz and we make no unsafe
+ // decisions based on the type. So even if we get out complete garbage
+ // we should still be safe.
+ let nhdr = take_nhdr(&mut self.base)?;
+ let name = take_bytes_align4(nhdr.n_namesz as usize, &mut self.base)?;
+ let desc = take_bytes_align4(nhdr.n_descsz as usize, &mut self.base)?;
+ Some(Note {
+ name: name,
+ desc: desc,
+ tipe: nhdr.n_type,
+ })
+ }
+}
+
+struct Perm(u32);
+
+/// Indicates that a segment is executable.
+const PERM_X: u32 = 0b00000001;
+/// Indicates that a segment is writable.
+const PERM_W: u32 = 0b00000010;
+/// Indicates that a segment is readable.
+const PERM_R: u32 = 0b00000100;
+
+impl core::fmt::Display for Perm {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let v = self.0;
+ if v & PERM_R != 0 {
+ f.write_char('r')?
+ }
+ if v & PERM_W != 0 {
+ f.write_char('w')?
+ }
+ if v & PERM_X != 0 {
+ f.write_char('x')?
+ }
+ Ok(())
+ }
+}
+
+/// Represents an ELF segment at runtime.
+struct Segment {
+ /// Gives the runtime virtual address of this segment's contents.
+ addr: usize,
+ /// Gives the memory size of this segment's contents.
+ size: usize,
+ /// Gives the module virtual address of this segment with the ELF file.
+ mod_rel_addr: usize,
+ /// Gives the permissions found in the ELF file. These permissions are not
+ /// necessarily the permissions present at runtime however.
+ flags: Perm,
+}
+
+/// Lets one iterate over Segments from a DSO.
+struct SegmentIter<'a> {
+ phdrs: &'a [Elf_Phdr],
+ base: usize,
+}
+
+impl Iterator for SegmentIter<'_> {
+ type Item = Segment;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.phdrs.split_first().and_then(|(phdr, new_phdrs)| {
+ self.phdrs = new_phdrs;
+ if phdr.p_type != PT_LOAD {
+ self.next()
+ } else {
+ Some(Segment {
+ addr: phdr.p_vaddr as usize + self.base,
+ size: phdr.p_memsz as usize,
+ mod_rel_addr: phdr.p_vaddr as usize,
+ flags: Perm(phdr.p_flags),
+ })
+ }
+ })
+ }
+}
+
+/// Represents an ELF DSO (Dynamic Shared Object). This type references
+/// the data stored in the actual DSO rather than making its own copy.
+struct Dso<'a> {
+ /// The dynamic linker always gives us a name, even if the name is empty.
+ /// In the case of the main executable this name will be empty. In the case
+ /// of a shared object it will be the soname (see DT_SONAME).
+ name: &'a str,
+ /// On Fuchsia virtually all binaries have build IDs but this is not a strict
+ /// requirement. There's no way to match up DSO information with a real ELF
+ /// file afterwards if there is no build_id so we require that every DSO
+ /// have one here. DSO's without a build_id are ignored.
+ build_id: &'a [u8],
+
+ base: usize,
+ phdrs: &'a [Elf_Phdr],
+}
+
+impl Dso<'_> {
+ /// Returns an iterator over Segments in this DSO.
+ fn segments(&self) -> SegmentIter<'_> {
+ SegmentIter {
+ phdrs: self.phdrs.as_ref(),
+ base: self.base,
+ }
+ }
+}
+
+struct HexSlice<'a> {
+ bytes: &'a [u8],
+}
+
+impl fmt::Display for HexSlice<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for byte in self.bytes {
+ write!(f, "{:02x}", byte)?;
+ }
+ Ok(())
+ }
+}
+
+fn get_build_id<'a>(info: &'a dl_phdr_info) -> Option<&'a [u8]> {
+ for phdr in info.program_headers() {
+ if phdr.phdr.p_type == PT_NOTE {
+ for note in phdr.notes() {
+ if note.tipe == NT_GNU_BUILD_ID && (note.name == b"GNU\0" || note.name == b"GNU") {
+ return Some(note.desc);
+ }
+ }
+ }
+ }
+ None
+}
+
+/// These errors encode issues that arise while parsing information about
+/// each DSO.
+enum Error {
+ /// NameError means that an error occurred while converting a C style string
+ /// into a rust string.
+ NameError(core::str::Utf8Error),
+ /// BuildIDError means that we didn't find a build ID. This could either be
+ /// because the DSO had no build ID or because the segment containing the
+ /// build ID was malformed.
+ BuildIDError,
+}
+
+/// Calls either 'dso' or 'error' for each DSO linked into the process by the
+/// dynamic linker.
+///
+/// # Arguments
+///
+/// * `visitor` - A DsoPrinter that will have one of eats methods called foreach DSO.
+fn for_each_dso(mut visitor: &mut DsoPrinter<'_, '_>) {
+ extern "C" fn callback(
+ info: &dl_phdr_info,
+ _size: usize,
+ visitor: &mut DsoPrinter<'_, '_>,
+ ) -> i32 {
+ // dl_iterate_phdr ensures that info.name will point to a valid
+ // location.
+ let name_len = unsafe { libc::strlen(info.name) };
+ let name_slice: &[u8] =
+ unsafe { core::slice::from_raw_parts(info.name as *const u8, name_len) };
+ let name = match core::str::from_utf8(name_slice) {
+ Ok(name) => name,
+ Err(err) => {
+ return visitor.error(Error::NameError(err)) as i32;
+ }
+ };
+ let build_id = match get_build_id(info) {
+ Some(build_id) => build_id,
+ None => {
+ return visitor.error(Error::BuildIDError) as i32;
+ }
+ };
+ visitor.dso(Dso {
+ name: name,
+ build_id: build_id,
+ phdrs: info.phdr_slice(),
+ base: info.addr as usize,
+ }) as i32
+ }
+ unsafe { dl_iterate_phdr(callback, &mut visitor) };
+}
+
+struct DsoPrinter<'a, 'b> {
+ writer: &'a mut core::fmt::Formatter<'b>,
+ module_count: usize,
+ error: core::fmt::Result,
+}
+
+impl DsoPrinter<'_, '_> {
+ fn dso(&mut self, dso: Dso<'_>) -> bool {
+ let mut write = || {
+ write!(
+ self.writer,
+ "{{{{{{module:{:#x}:{}:elf:{}}}}}}}\n",
+ self.module_count,
+ dso.name,
+ HexSlice {
+ bytes: dso.build_id.as_ref()
+ }
+ )?;
+ for seg in dso.segments() {
+ write!(
+ self.writer,
+ "{{{{{{mmap:{:#x}:{:#x}:load:{:#x}:{}:{:#x}}}}}}}\n",
+ seg.addr, seg.size, self.module_count, seg.flags, seg.mod_rel_addr
+ )?;
+ }
+ self.module_count += 1;
+ Ok(())
+ };
+ match write() {
+ Ok(()) => false,
+ Err(err) => {
+ self.error = Err(err);
+ true
+ }
+ }
+ }
+ fn error(&mut self, _error: Error) -> bool {
+ false
+ }
+}
+
+/// This function prints the Fuchsia symbolizer markup for all information contained in a DSO.
+pub fn print_dso_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ out.write_str("{{{reset}}}\n")?;
+ let mut visitor = DsoPrinter {
+ writer: out,
+ module_count: 0,
+ error: Ok(()),
+ };
+ for_each_dso(&mut visitor);
+ visitor.error
+}