diff options
Diffstat (limited to 'vendor/backtrace/src/print/fuchsia.rs')
-rw-r--r-- | vendor/backtrace/src/print/fuchsia.rs | 441 |
1 files changed, 0 insertions, 441 deletions
diff --git a/vendor/backtrace/src/print/fuchsia.rs b/vendor/backtrace/src/print/fuchsia.rs deleted file mode 100644 index cb87269..0000000 --- a/vendor/backtrace/src/print/fuchsia.rs +++ /dev/null @@ -1,441 +0,0 @@ -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. Eventually 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:begin}}}\n")?; - let mut visitor = DsoPrinter { - writer: out, - module_count: 0, - error: Ok(()), - }; - for_each_dso(&mut visitor); - visitor.error -} - -/// This function prints the Fuchsia symbolizer markup to end the backtrace. -pub fn finish_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - out.write_str("{{{reset:end}}}\n") -} |