Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
		
							
								
								
									
										4
									
								
								vendor/backtrace/src/android-api.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/backtrace/src/android-api.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
// Used from the build script to detect the value of the `__ANDROID_API__`
 | 
			
		||||
// builtin #define
 | 
			
		||||
 | 
			
		||||
APIVERSION __ANDROID_API__
 | 
			
		||||
							
								
								
									
										257
									
								
								vendor/backtrace/src/backtrace/dbghelp.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								vendor/backtrace/src/backtrace/dbghelp.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,257 @@
 | 
			
		||||
//! Backtrace strategy for MSVC platforms.
 | 
			
		||||
//!
 | 
			
		||||
//! This module contains the ability to generate a backtrace on MSVC using one
 | 
			
		||||
//! of two possible methods. The `StackWalkEx` function is primarily used if
 | 
			
		||||
//! possible, but not all systems have that. Failing that the `StackWalk64`
 | 
			
		||||
//! function is used instead. Note that `StackWalkEx` is favored because it
 | 
			
		||||
//! handles debuginfo internally and returns inline frame information.
 | 
			
		||||
//!
 | 
			
		||||
//! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs`
 | 
			
		||||
//! for more information about that.
 | 
			
		||||
 | 
			
		||||
#![allow(bad_style)]
 | 
			
		||||
 | 
			
		||||
use super::super::{dbghelp, windows::*};
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
use core::mem;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub enum StackFrame {
 | 
			
		||||
    New(STACKFRAME_EX),
 | 
			
		||||
    Old(STACKFRAME64),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy)]
 | 
			
		||||
pub struct Frame {
 | 
			
		||||
    pub(crate) stack_frame: StackFrame,
 | 
			
		||||
    base_address: *mut c_void,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// we're just sending around raw pointers and reading them, never interpreting
 | 
			
		||||
// them so this should be safe to both send and share across threads.
 | 
			
		||||
unsafe impl Send for Frame {}
 | 
			
		||||
unsafe impl Sync for Frame {}
 | 
			
		||||
 | 
			
		||||
impl Frame {
 | 
			
		||||
    pub fn ip(&self) -> *mut c_void {
 | 
			
		||||
        self.addr_pc().Offset as *mut _
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sp(&self) -> *mut c_void {
 | 
			
		||||
        self.addr_stack().Offset as *mut _
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        self.ip()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        Some(self.base_address)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn addr_pc(&self) -> &ADDRESS64 {
 | 
			
		||||
        match self.stack_frame {
 | 
			
		||||
            StackFrame::New(ref new) => &new.AddrPC,
 | 
			
		||||
            StackFrame::Old(ref old) => &old.AddrPC,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn addr_pc_mut(&mut self) -> &mut ADDRESS64 {
 | 
			
		||||
        match self.stack_frame {
 | 
			
		||||
            StackFrame::New(ref mut new) => &mut new.AddrPC,
 | 
			
		||||
            StackFrame::Old(ref mut old) => &mut old.AddrPC,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn addr_frame_mut(&mut self) -> &mut ADDRESS64 {
 | 
			
		||||
        match self.stack_frame {
 | 
			
		||||
            StackFrame::New(ref mut new) => &mut new.AddrFrame,
 | 
			
		||||
            StackFrame::Old(ref mut old) => &mut old.AddrFrame,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn addr_stack(&self) -> &ADDRESS64 {
 | 
			
		||||
        match self.stack_frame {
 | 
			
		||||
            StackFrame::New(ref new) => &new.AddrStack,
 | 
			
		||||
            StackFrame::Old(ref old) => &old.AddrStack,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn addr_stack_mut(&mut self) -> &mut ADDRESS64 {
 | 
			
		||||
        match self.stack_frame {
 | 
			
		||||
            StackFrame::New(ref mut new) => &mut new.AddrStack,
 | 
			
		||||
            StackFrame::Old(ref mut old) => &mut old.AddrStack,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now
 | 
			
		||||
struct MyContext(CONTEXT);
 | 
			
		||||
 | 
			
		||||
#[inline(always)]
 | 
			
		||||
pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
 | 
			
		||||
    // Allocate necessary structures for doing the stack walk
 | 
			
		||||
    let process = GetCurrentProcess();
 | 
			
		||||
    let thread = GetCurrentThread();
 | 
			
		||||
 | 
			
		||||
    let mut context = mem::zeroed::<MyContext>();
 | 
			
		||||
    RtlCaptureContext(&mut context.0);
 | 
			
		||||
 | 
			
		||||
    // Ensure this process's symbols are initialized
 | 
			
		||||
    let dbghelp = match dbghelp::init() {
 | 
			
		||||
        Ok(dbghelp) => dbghelp,
 | 
			
		||||
        Err(()) => return, // oh well...
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // On x86_64 and ARM64 we opt to not use the default `Sym*` functions from
 | 
			
		||||
    // dbghelp for getting the function table and module base. Instead we use
 | 
			
		||||
    // the `RtlLookupFunctionEntry` function in kernel32 which will account for
 | 
			
		||||
    // JIT compiler frames as well. These should be equivalent, but using
 | 
			
		||||
    // `Rtl*` allows us to backtrace through JIT frames.
 | 
			
		||||
    //
 | 
			
		||||
    // Note that `RtlLookupFunctionEntry` only works for in-process backtraces,
 | 
			
		||||
    // but that's all we support anyway, so it all lines up well.
 | 
			
		||||
    cfg_if::cfg_if! {
 | 
			
		||||
        if #[cfg(target_pointer_width = "64")] {
 | 
			
		||||
            use core::ptr;
 | 
			
		||||
 | 
			
		||||
            unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID {
 | 
			
		||||
                let mut base = 0;
 | 
			
		||||
                RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 {
 | 
			
		||||
                let mut base = 0;
 | 
			
		||||
                RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut());
 | 
			
		||||
                base
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            let function_table_access = dbghelp.SymFunctionTableAccess64();
 | 
			
		||||
            let get_module_base = dbghelp.SymGetModuleBase64();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let process_handle = GetCurrentProcess();
 | 
			
		||||
 | 
			
		||||
    // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64`
 | 
			
		||||
    // since it's in theory supported on more systems.
 | 
			
		||||
    match (*dbghelp.dbghelp()).StackWalkEx() {
 | 
			
		||||
        Some(StackWalkEx) => {
 | 
			
		||||
            let mut inner: STACKFRAME_EX = mem::zeroed();
 | 
			
		||||
            inner.StackFrameSize = mem::size_of::<STACKFRAME_EX>() as DWORD;
 | 
			
		||||
            let mut frame = super::Frame {
 | 
			
		||||
                inner: Frame {
 | 
			
		||||
                    stack_frame: StackFrame::New(inner),
 | 
			
		||||
                    base_address: 0 as _,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            let image = init_frame(&mut frame.inner, &context.0);
 | 
			
		||||
            let frame_ptr = match &mut frame.inner.stack_frame {
 | 
			
		||||
                StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX,
 | 
			
		||||
                _ => unreachable!(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            while StackWalkEx(
 | 
			
		||||
                image as DWORD,
 | 
			
		||||
                process,
 | 
			
		||||
                thread,
 | 
			
		||||
                frame_ptr,
 | 
			
		||||
                &mut context.0 as *mut CONTEXT as *mut _,
 | 
			
		||||
                None,
 | 
			
		||||
                Some(function_table_access),
 | 
			
		||||
                Some(get_module_base),
 | 
			
		||||
                None,
 | 
			
		||||
                0,
 | 
			
		||||
            ) == TRUE
 | 
			
		||||
            {
 | 
			
		||||
                frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
 | 
			
		||||
 | 
			
		||||
                if !cb(&frame) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None => {
 | 
			
		||||
            let mut frame = super::Frame {
 | 
			
		||||
                inner: Frame {
 | 
			
		||||
                    stack_frame: StackFrame::Old(mem::zeroed()),
 | 
			
		||||
                    base_address: 0 as _,
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            let image = init_frame(&mut frame.inner, &context.0);
 | 
			
		||||
            let frame_ptr = match &mut frame.inner.stack_frame {
 | 
			
		||||
                StackFrame::Old(ptr) => ptr as *mut STACKFRAME64,
 | 
			
		||||
                _ => unreachable!(),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            while dbghelp.StackWalk64()(
 | 
			
		||||
                image as DWORD,
 | 
			
		||||
                process,
 | 
			
		||||
                thread,
 | 
			
		||||
                frame_ptr,
 | 
			
		||||
                &mut context.0 as *mut CONTEXT as *mut _,
 | 
			
		||||
                None,
 | 
			
		||||
                Some(function_table_access),
 | 
			
		||||
                Some(get_module_base),
 | 
			
		||||
                None,
 | 
			
		||||
            ) == TRUE
 | 
			
		||||
            {
 | 
			
		||||
                frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _;
 | 
			
		||||
 | 
			
		||||
                if !cb(&frame) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "x86_64")]
 | 
			
		||||
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
 | 
			
		||||
    frame.addr_pc_mut().Offset = ctx.Rip as u64;
 | 
			
		||||
    frame.addr_pc_mut().Mode = AddrModeFlat;
 | 
			
		||||
    frame.addr_stack_mut().Offset = ctx.Rsp as u64;
 | 
			
		||||
    frame.addr_stack_mut().Mode = AddrModeFlat;
 | 
			
		||||
    frame.addr_frame_mut().Offset = ctx.Rbp as u64;
 | 
			
		||||
    frame.addr_frame_mut().Mode = AddrModeFlat;
 | 
			
		||||
 | 
			
		||||
    IMAGE_FILE_MACHINE_AMD64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "x86")]
 | 
			
		||||
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
 | 
			
		||||
    frame.addr_pc_mut().Offset = ctx.Eip as u64;
 | 
			
		||||
    frame.addr_pc_mut().Mode = AddrModeFlat;
 | 
			
		||||
    frame.addr_stack_mut().Offset = ctx.Esp as u64;
 | 
			
		||||
    frame.addr_stack_mut().Mode = AddrModeFlat;
 | 
			
		||||
    frame.addr_frame_mut().Offset = ctx.Ebp as u64;
 | 
			
		||||
    frame.addr_frame_mut().Mode = AddrModeFlat;
 | 
			
		||||
 | 
			
		||||
    IMAGE_FILE_MACHINE_I386
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "aarch64")]
 | 
			
		||||
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
 | 
			
		||||
    frame.addr_pc_mut().Offset = ctx.Pc as u64;
 | 
			
		||||
    frame.addr_pc_mut().Mode = AddrModeFlat;
 | 
			
		||||
    frame.addr_stack_mut().Offset = ctx.Sp as u64;
 | 
			
		||||
    frame.addr_stack_mut().Mode = AddrModeFlat;
 | 
			
		||||
    unsafe {
 | 
			
		||||
        frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64;
 | 
			
		||||
    }
 | 
			
		||||
    frame.addr_frame_mut().Mode = AddrModeFlat;
 | 
			
		||||
    IMAGE_FILE_MACHINE_ARM64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "arm")]
 | 
			
		||||
fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD {
 | 
			
		||||
    frame.addr_pc_mut().Offset = ctx.Pc as u64;
 | 
			
		||||
    frame.addr_pc_mut().Mode = AddrModeFlat;
 | 
			
		||||
    frame.addr_stack_mut().Offset = ctx.Sp as u64;
 | 
			
		||||
    frame.addr_stack_mut().Mode = AddrModeFlat;
 | 
			
		||||
    unsafe {
 | 
			
		||||
        frame.addr_frame_mut().Offset = ctx.R11 as u64;
 | 
			
		||||
    }
 | 
			
		||||
    frame.addr_frame_mut().Mode = AddrModeFlat;
 | 
			
		||||
    IMAGE_FILE_MACHINE_ARMNT
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										268
									
								
								vendor/backtrace/src/backtrace/libunwind.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								vendor/backtrace/src/backtrace/libunwind.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,268 @@
 | 
			
		||||
//! Backtrace support using libunwind/gcc_s/etc APIs.
 | 
			
		||||
//!
 | 
			
		||||
//! This module contains the ability to unwind the stack using libunwind-style
 | 
			
		||||
//! APIs. Note that there's a whole bunch of implementations of the
 | 
			
		||||
//! libunwind-like API, and this is just trying to be compatible with most of
 | 
			
		||||
//! them all at once instead of being picky.
 | 
			
		||||
//!
 | 
			
		||||
//! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very
 | 
			
		||||
//! reliable at generating a backtrace. It's not entirely clear how it does it
 | 
			
		||||
//! (frame pointers? eh_frame info? both?) but it seems to work!
 | 
			
		||||
//!
 | 
			
		||||
//! Most of the complexity of this module is handling the various platform
 | 
			
		||||
//! differences across libunwind implementations. Otherwise this is a pretty
 | 
			
		||||
//! straightforward Rust binding to the libunwind APIs.
 | 
			
		||||
//!
 | 
			
		||||
//! This is the default unwinding API for all non-Windows platforms currently.
 | 
			
		||||
 | 
			
		||||
use super::super::Bomb;
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
 | 
			
		||||
pub enum Frame {
 | 
			
		||||
    Raw(*mut uw::_Unwind_Context),
 | 
			
		||||
    Cloned {
 | 
			
		||||
        ip: *mut c_void,
 | 
			
		||||
        sp: *mut c_void,
 | 
			
		||||
        symbol_address: *mut c_void,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// With a raw libunwind pointer it should only ever be access in a readonly
 | 
			
		||||
// threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone`
 | 
			
		||||
// we always switch to a version which doesn't retain interior pointers, so we
 | 
			
		||||
// should be `Send` as well.
 | 
			
		||||
unsafe impl Send for Frame {}
 | 
			
		||||
unsafe impl Sync for Frame {}
 | 
			
		||||
 | 
			
		||||
impl Frame {
 | 
			
		||||
    pub fn ip(&self) -> *mut c_void {
 | 
			
		||||
        let ctx = match *self {
 | 
			
		||||
            Frame::Raw(ctx) => ctx,
 | 
			
		||||
            Frame::Cloned { ip, .. } => return ip,
 | 
			
		||||
        };
 | 
			
		||||
        unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sp(&self) -> *mut c_void {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Frame::Raw(ctx) => unsafe { uw::get_sp(ctx) as *mut c_void },
 | 
			
		||||
            Frame::Cloned { sp, .. } => sp,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        if let Frame::Cloned { symbol_address, .. } = *self {
 | 
			
		||||
            return symbol_address;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The macOS linker emits a "compact" unwind table that only includes an
 | 
			
		||||
        // entry for a function if that function either has an LSDA or its
 | 
			
		||||
        // encoding differs from that of the previous entry.  Consequently, on
 | 
			
		||||
        // macOS, `_Unwind_FindEnclosingFunction` is unreliable (it can return a
 | 
			
		||||
        // pointer to some totally unrelated function).  Instead, we just always
 | 
			
		||||
        // return the ip.
 | 
			
		||||
        //
 | 
			
		||||
        // https://github.com/rust-lang/rust/issues/74771#issuecomment-664056788
 | 
			
		||||
        //
 | 
			
		||||
        // Note the `skip_inner_frames.rs` test is skipped on macOS due to this
 | 
			
		||||
        // clause, and if this is fixed that test in theory can be run on macOS!
 | 
			
		||||
        if cfg!(target_vendor = "apple") {
 | 
			
		||||
            self.ip()
 | 
			
		||||
        } else {
 | 
			
		||||
            unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Clone for Frame {
 | 
			
		||||
    fn clone(&self) -> Frame {
 | 
			
		||||
        Frame::Cloned {
 | 
			
		||||
            ip: self.ip(),
 | 
			
		||||
            sp: self.sp(),
 | 
			
		||||
            symbol_address: self.symbol_address(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[inline(always)]
 | 
			
		||||
pub unsafe fn trace(mut cb: &mut dyn FnMut(&super::Frame) -> bool) {
 | 
			
		||||
    uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _);
 | 
			
		||||
 | 
			
		||||
    extern "C" fn trace_fn(
 | 
			
		||||
        ctx: *mut uw::_Unwind_Context,
 | 
			
		||||
        arg: *mut c_void,
 | 
			
		||||
    ) -> uw::_Unwind_Reason_Code {
 | 
			
		||||
        let cb = unsafe { &mut *(arg as *mut &mut dyn FnMut(&super::Frame) -> bool) };
 | 
			
		||||
        let cx = super::Frame {
 | 
			
		||||
            inner: Frame::Raw(ctx),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let mut bomb = Bomb { enabled: true };
 | 
			
		||||
        let keep_going = cb(&cx);
 | 
			
		||||
        bomb.enabled = false;
 | 
			
		||||
 | 
			
		||||
        if keep_going {
 | 
			
		||||
            uw::_URC_NO_REASON
 | 
			
		||||
        } else {
 | 
			
		||||
            uw::_URC_FAILURE
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Unwind library interface used for backtraces
 | 
			
		||||
///
 | 
			
		||||
/// Note that dead code is allowed as here are just bindings
 | 
			
		||||
/// iOS doesn't use all of them it but adding more
 | 
			
		||||
/// platform-specific configs pollutes the code too much
 | 
			
		||||
#[allow(non_camel_case_types)]
 | 
			
		||||
#[allow(non_snake_case)]
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
mod uw {
 | 
			
		||||
    pub use self::_Unwind_Reason_Code::*;
 | 
			
		||||
 | 
			
		||||
    use core::ffi::c_void;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub enum _Unwind_Reason_Code {
 | 
			
		||||
        _URC_NO_REASON = 0,
 | 
			
		||||
        _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
 | 
			
		||||
        _URC_FATAL_PHASE2_ERROR = 2,
 | 
			
		||||
        _URC_FATAL_PHASE1_ERROR = 3,
 | 
			
		||||
        _URC_NORMAL_STOP = 4,
 | 
			
		||||
        _URC_END_OF_STACK = 5,
 | 
			
		||||
        _URC_HANDLER_FOUND = 6,
 | 
			
		||||
        _URC_INSTALL_CONTEXT = 7,
 | 
			
		||||
        _URC_CONTINUE_UNWIND = 8,
 | 
			
		||||
        _URC_FAILURE = 9, // used only by ARM EABI
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub enum _Unwind_Context {}
 | 
			
		||||
 | 
			
		||||
    pub type _Unwind_Trace_Fn =
 | 
			
		||||
        extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
 | 
			
		||||
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        pub fn _Unwind_Backtrace(
 | 
			
		||||
            trace: _Unwind_Trace_Fn,
 | 
			
		||||
            trace_argument: *mut c_void,
 | 
			
		||||
        ) -> _Unwind_Reason_Code;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cfg_if::cfg_if! {
 | 
			
		||||
        // available since GCC 4.2.0, should be fine for our purpose
 | 
			
		||||
        if #[cfg(all(
 | 
			
		||||
            not(all(target_os = "android", target_arch = "arm")),
 | 
			
		||||
            not(all(target_os = "freebsd", target_arch = "arm")),
 | 
			
		||||
            not(all(target_os = "linux", target_arch = "arm")),
 | 
			
		||||
            not(all(target_os = "horizon", target_arch = "arm")),
 | 
			
		||||
            not(all(target_os = "vita", target_arch = "arm")),
 | 
			
		||||
        ))] {
 | 
			
		||||
            extern "C" {
 | 
			
		||||
                pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
 | 
			
		||||
                pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
 | 
			
		||||
 | 
			
		||||
                #[cfg(not(all(target_os = "linux", target_arch = "s390x")))]
 | 
			
		||||
                // This function is a misnomer: rather than getting this frame's
 | 
			
		||||
                // Canonical Frame Address (aka the caller frame's SP) it
 | 
			
		||||
                // returns this frame's SP.
 | 
			
		||||
                //
 | 
			
		||||
                // https://github.com/libunwind/libunwind/blob/d32956507cf29d9b1a98a8bce53c78623908f4fe/src/unwind/GetCFA.c#L28-L35
 | 
			
		||||
                #[link_name = "_Unwind_GetCFA"]
 | 
			
		||||
                pub fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // s390x uses a biased CFA value, therefore we need to use
 | 
			
		||||
            // _Unwind_GetGR to get the stack pointer register (%r15)
 | 
			
		||||
            // instead of relying on _Unwind_GetCFA.
 | 
			
		||||
            #[cfg(all(target_os = "linux", target_arch = "s390x"))]
 | 
			
		||||
            pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
 | 
			
		||||
                extern "C" {
 | 
			
		||||
                    pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, index: libc::c_int) -> libc::uintptr_t;
 | 
			
		||||
                }
 | 
			
		||||
                _Unwind_GetGR(ctx, 15)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // On android and arm, the function `_Unwind_GetIP` and a bunch of
 | 
			
		||||
            // others are macros, so we define functions containing the
 | 
			
		||||
            // expansion of the macros.
 | 
			
		||||
            //
 | 
			
		||||
            // TODO: link to the header file that defines these macros, if you
 | 
			
		||||
            // can find it. (I, fitzgen, cannot find the header file that some
 | 
			
		||||
            // of these macro expansions were originally borrowed from.)
 | 
			
		||||
            #[repr(C)]
 | 
			
		||||
            enum _Unwind_VRS_Result {
 | 
			
		||||
                _UVRSR_OK = 0,
 | 
			
		||||
                _UVRSR_NOT_IMPLEMENTED = 1,
 | 
			
		||||
                _UVRSR_FAILED = 2,
 | 
			
		||||
            }
 | 
			
		||||
            #[repr(C)]
 | 
			
		||||
            enum _Unwind_VRS_RegClass {
 | 
			
		||||
                _UVRSC_CORE = 0,
 | 
			
		||||
                _UVRSC_VFP = 1,
 | 
			
		||||
                _UVRSC_FPA = 2,
 | 
			
		||||
                _UVRSC_WMMXD = 3,
 | 
			
		||||
                _UVRSC_WMMXC = 4,
 | 
			
		||||
            }
 | 
			
		||||
            #[repr(C)]
 | 
			
		||||
            enum _Unwind_VRS_DataRepresentation {
 | 
			
		||||
                _UVRSD_UINT32 = 0,
 | 
			
		||||
                _UVRSD_VFPX = 1,
 | 
			
		||||
                _UVRSD_FPAX = 2,
 | 
			
		||||
                _UVRSD_UINT64 = 3,
 | 
			
		||||
                _UVRSD_FLOAT = 4,
 | 
			
		||||
                _UVRSD_DOUBLE = 5,
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            type _Unwind_Word = libc::c_uint;
 | 
			
		||||
            extern "C" {
 | 
			
		||||
                fn _Unwind_VRS_Get(
 | 
			
		||||
                    ctx: *mut _Unwind_Context,
 | 
			
		||||
                    klass: _Unwind_VRS_RegClass,
 | 
			
		||||
                    word: _Unwind_Word,
 | 
			
		||||
                    repr: _Unwind_VRS_DataRepresentation,
 | 
			
		||||
                    data: *mut c_void,
 | 
			
		||||
                ) -> _Unwind_VRS_Result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
 | 
			
		||||
                let mut val: _Unwind_Word = 0;
 | 
			
		||||
                let ptr = &mut val as *mut _Unwind_Word;
 | 
			
		||||
                let _ = _Unwind_VRS_Get(
 | 
			
		||||
                    ctx,
 | 
			
		||||
                    _Unwind_VRS_RegClass::_UVRSC_CORE,
 | 
			
		||||
                    15,
 | 
			
		||||
                    _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
 | 
			
		||||
                    ptr as *mut c_void,
 | 
			
		||||
                );
 | 
			
		||||
                (val & !1) as libc::uintptr_t
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // R13 is the stack pointer on arm.
 | 
			
		||||
            const SP: _Unwind_Word = 13;
 | 
			
		||||
 | 
			
		||||
            pub unsafe fn get_sp(ctx: *mut _Unwind_Context) -> libc::uintptr_t {
 | 
			
		||||
                let mut val: _Unwind_Word = 0;
 | 
			
		||||
                let ptr = &mut val as *mut _Unwind_Word;
 | 
			
		||||
                let _ = _Unwind_VRS_Get(
 | 
			
		||||
                    ctx,
 | 
			
		||||
                    _Unwind_VRS_RegClass::_UVRSC_CORE,
 | 
			
		||||
                    SP,
 | 
			
		||||
                    _Unwind_VRS_DataRepresentation::_UVRSD_UINT32,
 | 
			
		||||
                    ptr as *mut c_void,
 | 
			
		||||
                );
 | 
			
		||||
                val as libc::uintptr_t
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // This function also doesn't exist on Android or ARM/Linux, so make it
 | 
			
		||||
            // a no-op.
 | 
			
		||||
            pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
 | 
			
		||||
                pc
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										109
									
								
								vendor/backtrace/src/backtrace/miri.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								vendor/backtrace/src/backtrace/miri.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
use alloc::boxed::Box;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
 | 
			
		||||
extern "Rust" {
 | 
			
		||||
    fn miri_backtrace_size(flags: u64) -> usize;
 | 
			
		||||
    fn miri_get_backtrace(flags: u64, buf: *mut *mut ());
 | 
			
		||||
    fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
 | 
			
		||||
    fn miri_resolve_frame_names(ptr: *mut (), flags: u64, name_buf: *mut u8, filename_buf: *mut u8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct MiriFrame {
 | 
			
		||||
    pub name_len: usize,
 | 
			
		||||
    pub filename_len: usize,
 | 
			
		||||
    pub lineno: u32,
 | 
			
		||||
    pub colno: u32,
 | 
			
		||||
    pub fn_ptr: *mut c_void,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct FullMiriFrame {
 | 
			
		||||
    pub name: Box<[u8]>,
 | 
			
		||||
    pub filename: Box<[u8]>,
 | 
			
		||||
    pub lineno: u32,
 | 
			
		||||
    pub colno: u32,
 | 
			
		||||
    pub fn_ptr: *mut c_void,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct Frame {
 | 
			
		||||
    pub addr: *mut c_void,
 | 
			
		||||
    pub inner: FullMiriFrame,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SAFETY: Miri guarantees that the returned pointer
 | 
			
		||||
// can be used from any thread.
 | 
			
		||||
unsafe impl Send for Frame {}
 | 
			
		||||
unsafe impl Sync for Frame {}
 | 
			
		||||
 | 
			
		||||
impl Frame {
 | 
			
		||||
    pub fn ip(&self) -> *mut c_void {
 | 
			
		||||
        self.addr
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sp(&self) -> *mut c_void {
 | 
			
		||||
        core::ptr::null_mut()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        self.inner.fn_ptr
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn trace<F: FnMut(&super::Frame) -> bool>(cb: F) {
 | 
			
		||||
    // SAFETY: Miri guarantees that the backtrace API functions
 | 
			
		||||
    // can be called from any thread.
 | 
			
		||||
    unsafe { trace_unsynchronized(cb) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn resolve_addr(ptr: *mut c_void) -> Frame {
 | 
			
		||||
    // SAFETY: Miri will stop execution with an error if this pointer
 | 
			
		||||
    // is invalid.
 | 
			
		||||
    let frame = unsafe { miri_resolve_frame(ptr as *mut (), 1) };
 | 
			
		||||
 | 
			
		||||
    let mut name = Vec::with_capacity(frame.name_len);
 | 
			
		||||
    let mut filename = Vec::with_capacity(frame.filename_len);
 | 
			
		||||
 | 
			
		||||
    // SAFETY: name and filename have been allocated with the amount
 | 
			
		||||
    // of memory miri has asked for, and miri guarantees it will initialize it
 | 
			
		||||
    unsafe {
 | 
			
		||||
        miri_resolve_frame_names(ptr as *mut (), 0, name.as_mut_ptr(), filename.as_mut_ptr());
 | 
			
		||||
 | 
			
		||||
        name.set_len(frame.name_len);
 | 
			
		||||
        filename.set_len(frame.filename_len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Frame {
 | 
			
		||||
        addr: ptr,
 | 
			
		||||
        inner: FullMiriFrame {
 | 
			
		||||
            name: name.into(),
 | 
			
		||||
            filename: filename.into(),
 | 
			
		||||
            lineno: frame.lineno,
 | 
			
		||||
            colno: frame.colno,
 | 
			
		||||
            fn_ptr: frame.fn_ptr,
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn trace_unsynchronized<F: FnMut(&super::Frame) -> bool>(mut cb: F) {
 | 
			
		||||
    let len = miri_backtrace_size(0);
 | 
			
		||||
 | 
			
		||||
    let mut frames = Vec::with_capacity(len);
 | 
			
		||||
 | 
			
		||||
    miri_get_backtrace(1, frames.as_mut_ptr());
 | 
			
		||||
 | 
			
		||||
    frames.set_len(len);
 | 
			
		||||
 | 
			
		||||
    for ptr in frames.iter() {
 | 
			
		||||
        let frame = resolve_addr(*ptr as *mut c_void);
 | 
			
		||||
        if !cb(&super::Frame { inner: frame }) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								vendor/backtrace/src/backtrace/mod.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								vendor/backtrace/src/backtrace/mod.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,163 @@
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
use core::fmt;
 | 
			
		||||
 | 
			
		||||
/// Inspects the current call-stack, passing all active frames into the closure
 | 
			
		||||
/// provided to calculate a stack trace.
 | 
			
		||||
///
 | 
			
		||||
/// This function is the workhorse of this library in calculating the stack
 | 
			
		||||
/// traces for a program. The given closure `cb` is yielded instances of a
 | 
			
		||||
/// `Frame` which represent information about that call frame on the stack. The
 | 
			
		||||
/// closure is yielded frames in a top-down fashion (most recently called
 | 
			
		||||
/// functions first).
 | 
			
		||||
///
 | 
			
		||||
/// The closure's return value is an indication of whether the backtrace should
 | 
			
		||||
/// continue. A return value of `false` will terminate the backtrace and return
 | 
			
		||||
/// immediately.
 | 
			
		||||
///
 | 
			
		||||
/// Once a `Frame` is acquired you will likely want to call `backtrace::resolve`
 | 
			
		||||
/// to convert the `ip` (instruction pointer) or symbol address to a `Symbol`
 | 
			
		||||
/// through which the name and/or filename/line number can be learned.
 | 
			
		||||
///
 | 
			
		||||
/// Note that this is a relatively low-level function and if you'd like to, for
 | 
			
		||||
/// example, capture a backtrace to be inspected later, then the `Backtrace`
 | 
			
		||||
/// type may be more appropriate.
 | 
			
		||||
///
 | 
			
		||||
/// # Required features
 | 
			
		||||
///
 | 
			
		||||
/// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
/// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
///
 | 
			
		||||
/// # Panics
 | 
			
		||||
///
 | 
			
		||||
/// This function strives to never panic, but if the `cb` provided panics then
 | 
			
		||||
/// some platforms will force a double panic to abort the process. Some
 | 
			
		||||
/// platforms use a C library which internally uses callbacks which cannot be
 | 
			
		||||
/// unwound through, so panicking from `cb` may trigger a process abort.
 | 
			
		||||
///
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// extern crate backtrace;
 | 
			
		||||
///
 | 
			
		||||
/// fn main() {
 | 
			
		||||
///     backtrace::trace(|frame| {
 | 
			
		||||
///         // ...
 | 
			
		||||
///
 | 
			
		||||
///         true // continue the backtrace
 | 
			
		||||
///     });
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub fn trace<F: FnMut(&Frame) -> bool>(cb: F) {
 | 
			
		||||
    let _guard = crate::lock::lock();
 | 
			
		||||
    unsafe { trace_unsynchronized(cb) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Same as `trace`, only unsafe as it's unsynchronized.
 | 
			
		||||
///
 | 
			
		||||
/// This function does not have synchronization guarantees but is available
 | 
			
		||||
/// when the `std` feature of this crate isn't compiled in. See the `trace`
 | 
			
		||||
/// function for more documentation and examples.
 | 
			
		||||
///
 | 
			
		||||
/// # Panics
 | 
			
		||||
///
 | 
			
		||||
/// See information on `trace` for caveats on `cb` panicking.
 | 
			
		||||
pub unsafe fn trace_unsynchronized<F: FnMut(&Frame) -> bool>(mut cb: F) {
 | 
			
		||||
    trace_imp(&mut cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A trait representing one frame of a backtrace, yielded to the `trace`
 | 
			
		||||
/// function of this crate.
 | 
			
		||||
///
 | 
			
		||||
/// The tracing function's closure will be yielded frames, and the frame is
 | 
			
		||||
/// virtually dispatched as the underlying implementation is not always known
 | 
			
		||||
/// until runtime.
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Frame {
 | 
			
		||||
    pub(crate) inner: FrameImp,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Frame {
 | 
			
		||||
    /// Returns the current instruction pointer of this frame.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is normally the next instruction to execute in the frame, but not
 | 
			
		||||
    /// all implementations list this with 100% accuracy (but it's generally
 | 
			
		||||
    /// pretty close).
 | 
			
		||||
    ///
 | 
			
		||||
    /// It is recommended to pass this value to `backtrace::resolve` to turn it
 | 
			
		||||
    /// into a symbol name.
 | 
			
		||||
    pub fn ip(&self) -> *mut c_void {
 | 
			
		||||
        self.inner.ip()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the current stack pointer of this frame.
 | 
			
		||||
    ///
 | 
			
		||||
    /// In the case that a backend cannot recover the stack pointer for this
 | 
			
		||||
    /// frame, a null pointer is returned.
 | 
			
		||||
    pub fn sp(&self) -> *mut c_void {
 | 
			
		||||
        self.inner.sp()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the starting symbol address of the frame of this function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This will attempt to rewind the instruction pointer returned by `ip` to
 | 
			
		||||
    /// the start of the function, returning that value. In some cases, however,
 | 
			
		||||
    /// backends will just return `ip` from this function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The returned value can sometimes be used if `backtrace::resolve` failed
 | 
			
		||||
    /// on the `ip` given above.
 | 
			
		||||
    pub fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        self.inner.symbol_address()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the base address of the module to which the frame belongs.
 | 
			
		||||
    pub fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        self.inner.module_base_address()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Frame {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        f.debug_struct("Frame")
 | 
			
		||||
            .field("ip", &self.ip())
 | 
			
		||||
            .field("symbol_address", &self.symbol_address())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    // This needs to come first, to ensure that
 | 
			
		||||
    // Miri takes priority over the host platform
 | 
			
		||||
    if #[cfg(miri)] {
 | 
			
		||||
        pub(crate) mod miri;
 | 
			
		||||
        use self::miri::trace as trace_imp;
 | 
			
		||||
        pub(crate) use self::miri::Frame as FrameImp;
 | 
			
		||||
    } else if #[cfg(
 | 
			
		||||
        any(
 | 
			
		||||
            all(
 | 
			
		||||
                unix,
 | 
			
		||||
                not(target_os = "emscripten"),
 | 
			
		||||
                not(all(target_os = "ios", target_arch = "arm")),
 | 
			
		||||
                not(all(target_os = "nto", target_env = "nto70")),
 | 
			
		||||
            ),
 | 
			
		||||
            all(
 | 
			
		||||
                target_env = "sgx",
 | 
			
		||||
                target_vendor = "fortanix",
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
    )] {
 | 
			
		||||
        mod libunwind;
 | 
			
		||||
        use self::libunwind::trace as trace_imp;
 | 
			
		||||
        pub(crate) use self::libunwind::Frame as FrameImp;
 | 
			
		||||
    } else if #[cfg(all(windows, not(target_vendor = "uwp")))] {
 | 
			
		||||
        mod dbghelp;
 | 
			
		||||
        use self::dbghelp::trace as trace_imp;
 | 
			
		||||
        pub(crate) use self::dbghelp::Frame as FrameImp;
 | 
			
		||||
        #[cfg(target_env = "msvc")] // only used in dbghelp symbolize
 | 
			
		||||
        pub(crate) use self::dbghelp::StackFrame;
 | 
			
		||||
    } else {
 | 
			
		||||
        mod noop;
 | 
			
		||||
        use self::noop::trace as trace_imp;
 | 
			
		||||
        pub(crate) use self::noop::Frame as FrameImp;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								vendor/backtrace/src/backtrace/noop.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/backtrace/src/backtrace/noop.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
//! Empty implementation of unwinding used when no other implementation is
 | 
			
		||||
//! appropriate.
 | 
			
		||||
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
 | 
			
		||||
#[inline(always)]
 | 
			
		||||
pub fn trace(_cb: &mut dyn FnMut(&super::Frame) -> bool) {}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct Frame;
 | 
			
		||||
 | 
			
		||||
impl Frame {
 | 
			
		||||
    pub fn ip(&self) -> *mut c_void {
 | 
			
		||||
        0 as *mut _
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn sp(&self) -> *mut c_void {
 | 
			
		||||
        0 as *mut _
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        0 as *mut _
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										555
									
								
								vendor/backtrace/src/capture.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										555
									
								
								vendor/backtrace/src/capture.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,555 @@
 | 
			
		||||
use crate::PrintFmt;
 | 
			
		||||
use crate::{resolve, resolve_frame, trace, BacktraceFmt, Symbol, SymbolName};
 | 
			
		||||
use std::ffi::c_void;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::prelude::v1::*;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "serde")]
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
/// Representation of an owned and self-contained backtrace.
 | 
			
		||||
///
 | 
			
		||||
/// This structure can be used to capture a backtrace at various points in a
 | 
			
		||||
/// program and later used to inspect what the backtrace was at that time.
 | 
			
		||||
///
 | 
			
		||||
/// `Backtrace` supports pretty-printing of backtraces through its `Debug`
 | 
			
		||||
/// implementation.
 | 
			
		||||
///
 | 
			
		||||
/// # Required features
 | 
			
		||||
///
 | 
			
		||||
/// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
/// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
#[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
 | 
			
		||||
pub struct Backtrace {
 | 
			
		||||
    // Frames here are listed from top-to-bottom of the stack
 | 
			
		||||
    frames: Vec<BacktraceFrame>,
 | 
			
		||||
    // The index we believe is the actual start of the backtrace, omitting
 | 
			
		||||
    // frames like `Backtrace::new` and `backtrace::trace`.
 | 
			
		||||
    actual_start_index: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn _assert_send_sync() {
 | 
			
		||||
    fn _assert<T: Send + Sync>() {}
 | 
			
		||||
    _assert::<Backtrace>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Captured version of a frame in a backtrace.
 | 
			
		||||
///
 | 
			
		||||
/// This type is returned as a list from `Backtrace::frames` and represents one
 | 
			
		||||
/// stack frame in a captured backtrace.
 | 
			
		||||
///
 | 
			
		||||
/// # Required features
 | 
			
		||||
///
 | 
			
		||||
/// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
/// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct BacktraceFrame {
 | 
			
		||||
    frame: Frame,
 | 
			
		||||
    symbols: Option<Vec<BacktraceSymbol>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
enum Frame {
 | 
			
		||||
    Raw(crate::Frame),
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    Deserialized {
 | 
			
		||||
        ip: usize,
 | 
			
		||||
        symbol_address: usize,
 | 
			
		||||
        module_base_address: Option<usize>,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Frame {
 | 
			
		||||
    fn ip(&self) -> *mut c_void {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Frame::Raw(ref f) => f.ip(),
 | 
			
		||||
            Frame::Deserialized { ip, .. } => ip as *mut c_void,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Frame::Raw(ref f) => f.symbol_address(),
 | 
			
		||||
            Frame::Deserialized { symbol_address, .. } => symbol_address as *mut c_void,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Frame::Raw(ref f) => f.module_base_address(),
 | 
			
		||||
            Frame::Deserialized {
 | 
			
		||||
                module_base_address,
 | 
			
		||||
                ..
 | 
			
		||||
            } => module_base_address.map(|addr| addr as *mut c_void),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Captured version of a symbol in a backtrace.
 | 
			
		||||
///
 | 
			
		||||
/// This type is returned as a list from `BacktraceFrame::symbols` and
 | 
			
		||||
/// represents the metadata for a symbol in a backtrace.
 | 
			
		||||
///
 | 
			
		||||
/// # Required features
 | 
			
		||||
///
 | 
			
		||||
/// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
/// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
#[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))]
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
 | 
			
		||||
pub struct BacktraceSymbol {
 | 
			
		||||
    name: Option<Vec<u8>>,
 | 
			
		||||
    addr: Option<usize>,
 | 
			
		||||
    filename: Option<PathBuf>,
 | 
			
		||||
    lineno: Option<u32>,
 | 
			
		||||
    colno: Option<u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Backtrace {
 | 
			
		||||
    /// Captures a backtrace at the callsite of this function, returning an
 | 
			
		||||
    /// owned representation.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function is useful for representing a backtrace as an object in
 | 
			
		||||
    /// Rust. This returned value can be sent across threads and printed
 | 
			
		||||
    /// elsewhere, and the purpose of this value is to be entirely self
 | 
			
		||||
    /// contained.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that on some platforms acquiring a full backtrace and resolving it
 | 
			
		||||
    /// can be extremely expensive. If the cost is too much for your application
 | 
			
		||||
    /// it's recommended to instead use `Backtrace::new_unresolved()` which
 | 
			
		||||
    /// avoids the symbol resolution step (which typically takes the longest)
 | 
			
		||||
    /// and allows deferring that to a later date.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use backtrace::Backtrace;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let current_backtrace = Backtrace::new();
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    #[inline(never)] // want to make sure there's a frame here to remove
 | 
			
		||||
    pub fn new() -> Backtrace {
 | 
			
		||||
        let mut bt = Self::create(Self::new as usize);
 | 
			
		||||
        bt.resolve();
 | 
			
		||||
        bt
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Similar to `new` except that this does not resolve any symbols, this
 | 
			
		||||
    /// simply captures the backtrace as a list of addresses.
 | 
			
		||||
    ///
 | 
			
		||||
    /// At a later time the `resolve` function can be called to resolve this
 | 
			
		||||
    /// backtrace's symbols into readable names. This function exists because
 | 
			
		||||
    /// the resolution process can sometimes take a significant amount of time
 | 
			
		||||
    /// whereas any one backtrace may only be rarely printed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use backtrace::Backtrace;
 | 
			
		||||
    ///
 | 
			
		||||
    /// let mut current_backtrace = Backtrace::new_unresolved();
 | 
			
		||||
    /// println!("{:?}", current_backtrace); // no symbol names
 | 
			
		||||
    /// current_backtrace.resolve();
 | 
			
		||||
    /// println!("{:?}", current_backtrace); // symbol names now present
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    #[inline(never)] // want to make sure there's a frame here to remove
 | 
			
		||||
    pub fn new_unresolved() -> Backtrace {
 | 
			
		||||
        Self::create(Self::new_unresolved as usize)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create(ip: usize) -> Backtrace {
 | 
			
		||||
        let mut frames = Vec::new();
 | 
			
		||||
        let mut actual_start_index = None;
 | 
			
		||||
        trace(|frame| {
 | 
			
		||||
            frames.push(BacktraceFrame {
 | 
			
		||||
                frame: Frame::Raw(frame.clone()),
 | 
			
		||||
                symbols: None,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if frame.symbol_address() as usize == ip && actual_start_index.is_none() {
 | 
			
		||||
                actual_start_index = Some(frames.len());
 | 
			
		||||
            }
 | 
			
		||||
            true
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        Backtrace {
 | 
			
		||||
            frames,
 | 
			
		||||
            actual_start_index: actual_start_index.unwrap_or(0),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the frames from when this backtrace was captured.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The first entry of this slice is likely the function `Backtrace::new`,
 | 
			
		||||
    /// and the last frame is likely something about how this thread or the main
 | 
			
		||||
    /// function started.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn frames(&self) -> &[BacktraceFrame] {
 | 
			
		||||
        &self.frames[self.actual_start_index..]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this backtrace was created from `new_unresolved` then this function
 | 
			
		||||
    /// will resolve all addresses in the backtrace to their symbolic names.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If this backtrace has been previously resolved or was created through
 | 
			
		||||
    /// `new`, this function does nothing.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn resolve(&mut self) {
 | 
			
		||||
        for frame in self.frames.iter_mut().filter(|f| f.symbols.is_none()) {
 | 
			
		||||
            let mut symbols = Vec::new();
 | 
			
		||||
            {
 | 
			
		||||
                let sym = |symbol: &Symbol| {
 | 
			
		||||
                    symbols.push(BacktraceSymbol {
 | 
			
		||||
                        name: symbol.name().map(|m| m.as_bytes().to_vec()),
 | 
			
		||||
                        addr: symbol.addr().map(|a| a as usize),
 | 
			
		||||
                        filename: symbol.filename().map(|m| m.to_owned()),
 | 
			
		||||
                        lineno: symbol.lineno(),
 | 
			
		||||
                        colno: symbol.colno(),
 | 
			
		||||
                    });
 | 
			
		||||
                };
 | 
			
		||||
                match frame.frame {
 | 
			
		||||
                    Frame::Raw(ref f) => resolve_frame(f, sym),
 | 
			
		||||
                    Frame::Deserialized { ip, .. } => {
 | 
			
		||||
                        resolve(ip as *mut c_void, sym);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            frame.symbols = Some(symbols);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Vec<BacktraceFrame>> for Backtrace {
 | 
			
		||||
    fn from(frames: Vec<BacktraceFrame>) -> Self {
 | 
			
		||||
        Backtrace {
 | 
			
		||||
            frames,
 | 
			
		||||
            actual_start_index: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<crate::Frame> for BacktraceFrame {
 | 
			
		||||
    fn from(frame: crate::Frame) -> BacktraceFrame {
 | 
			
		||||
        BacktraceFrame {
 | 
			
		||||
            frame: Frame::Raw(frame),
 | 
			
		||||
            symbols: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Into<Vec<BacktraceFrame>> for Backtrace {
 | 
			
		||||
    fn into(self) -> Vec<BacktraceFrame> {
 | 
			
		||||
        self.frames
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BacktraceFrame {
 | 
			
		||||
    /// Same as `Frame::ip`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn ip(&self) -> *mut c_void {
 | 
			
		||||
        self.frame.ip() as *mut c_void
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Same as `Frame::symbol_address`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn symbol_address(&self) -> *mut c_void {
 | 
			
		||||
        self.frame.symbol_address() as *mut c_void
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Same as `Frame::module_base_address`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn module_base_address(&self) -> Option<*mut c_void> {
 | 
			
		||||
        self.frame
 | 
			
		||||
            .module_base_address()
 | 
			
		||||
            .map(|addr| addr as *mut c_void)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the list of symbols that this frame corresponds to.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Normally there is only one symbol per frame, but sometimes if a number
 | 
			
		||||
    /// of functions are inlined into one frame then multiple symbols will be
 | 
			
		||||
    /// returned. The first symbol listed is the "innermost function", whereas
 | 
			
		||||
    /// the last symbol is the outermost (last caller).
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that if this frame came from an unresolved backtrace then this will
 | 
			
		||||
    /// return an empty list.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn symbols(&self) -> &[BacktraceSymbol] {
 | 
			
		||||
        self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BacktraceSymbol {
 | 
			
		||||
    /// Same as `Symbol::name`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn name(&self) -> Option<SymbolName<'_>> {
 | 
			
		||||
        self.name.as_ref().map(|s| SymbolName::new(s))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Same as `Symbol::addr`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn addr(&self) -> Option<*mut c_void> {
 | 
			
		||||
        self.addr.map(|s| s as *mut c_void)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Same as `Symbol::filename`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn filename(&self) -> Option<&Path> {
 | 
			
		||||
        self.filename.as_ref().map(|p| &**p)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Same as `Symbol::lineno`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn lineno(&self) -> Option<u32> {
 | 
			
		||||
        self.lineno
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Same as `Symbol::colno`
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn colno(&self) -> Option<u32> {
 | 
			
		||||
        self.colno
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Backtrace {
 | 
			
		||||
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let full = fmt.alternate();
 | 
			
		||||
        let (frames, style) = if full {
 | 
			
		||||
            (&self.frames[..], PrintFmt::Full)
 | 
			
		||||
        } else {
 | 
			
		||||
            (&self.frames[self.actual_start_index..], PrintFmt::Short)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // When printing paths we try to strip the cwd if it exists, otherwise
 | 
			
		||||
        // we just print the path as-is. Note that we also only do this for the
 | 
			
		||||
        // short format, because if it's full we presumably want to print
 | 
			
		||||
        // everything.
 | 
			
		||||
        let cwd = std::env::current_dir();
 | 
			
		||||
        let mut print_path =
 | 
			
		||||
            move |fmt: &mut fmt::Formatter<'_>, path: crate::BytesOrWideString<'_>| {
 | 
			
		||||
                let path = path.into_path_buf();
 | 
			
		||||
                if !full {
 | 
			
		||||
                    if let Ok(cwd) = &cwd {
 | 
			
		||||
                        if let Ok(suffix) = path.strip_prefix(cwd) {
 | 
			
		||||
                            return fmt::Display::fmt(&suffix.display(), fmt);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                fmt::Display::fmt(&path.display(), fmt)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        let mut f = BacktraceFmt::new(fmt, style, &mut print_path);
 | 
			
		||||
        f.add_context()?;
 | 
			
		||||
        for frame in frames {
 | 
			
		||||
            f.frame().backtrace_frame(frame)?;
 | 
			
		||||
        }
 | 
			
		||||
        f.finish()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Backtrace {
 | 
			
		||||
    fn default() -> Backtrace {
 | 
			
		||||
        Backtrace::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for BacktraceFrame {
 | 
			
		||||
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        fmt.debug_struct("BacktraceFrame")
 | 
			
		||||
            .field("ip", &self.ip())
 | 
			
		||||
            .field("symbol_address", &self.symbol_address())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for BacktraceSymbol {
 | 
			
		||||
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        fmt.debug_struct("BacktraceSymbol")
 | 
			
		||||
            .field("name", &self.name())
 | 
			
		||||
            .field("addr", &self.addr())
 | 
			
		||||
            .field("filename", &self.filename())
 | 
			
		||||
            .field("lineno", &self.lineno())
 | 
			
		||||
            .field("colno", &self.colno())
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "serialize-rustc")]
 | 
			
		||||
mod rustc_serialize_impls {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 | 
			
		||||
 | 
			
		||||
    #[derive(RustcEncodable, RustcDecodable)]
 | 
			
		||||
    struct SerializedFrame {
 | 
			
		||||
        ip: usize,
 | 
			
		||||
        symbol_address: usize,
 | 
			
		||||
        module_base_address: Option<usize>,
 | 
			
		||||
        symbols: Option<Vec<BacktraceSymbol>>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Decodable for BacktraceFrame {
 | 
			
		||||
        fn decode<D>(d: &mut D) -> Result<Self, D::Error>
 | 
			
		||||
        where
 | 
			
		||||
            D: Decoder,
 | 
			
		||||
        {
 | 
			
		||||
            let frame: SerializedFrame = SerializedFrame::decode(d)?;
 | 
			
		||||
            Ok(BacktraceFrame {
 | 
			
		||||
                frame: Frame::Deserialized {
 | 
			
		||||
                    ip: frame.ip,
 | 
			
		||||
                    symbol_address: frame.symbol_address,
 | 
			
		||||
                    module_base_address: frame.module_base_address,
 | 
			
		||||
                },
 | 
			
		||||
                symbols: frame.symbols,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Encodable for BacktraceFrame {
 | 
			
		||||
        fn encode<E>(&self, e: &mut E) -> Result<(), E::Error>
 | 
			
		||||
        where
 | 
			
		||||
            E: Encoder,
 | 
			
		||||
        {
 | 
			
		||||
            let BacktraceFrame { frame, symbols } = self;
 | 
			
		||||
            SerializedFrame {
 | 
			
		||||
                ip: frame.ip() as usize,
 | 
			
		||||
                symbol_address: frame.symbol_address() as usize,
 | 
			
		||||
                module_base_address: frame.module_base_address().map(|addr| addr as usize),
 | 
			
		||||
                symbols: symbols.clone(),
 | 
			
		||||
            }
 | 
			
		||||
            .encode(e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "serde")]
 | 
			
		||||
mod serde_impls {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use serde::de::Deserializer;
 | 
			
		||||
    use serde::ser::Serializer;
 | 
			
		||||
    use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
    #[derive(Serialize, Deserialize)]
 | 
			
		||||
    struct SerializedFrame {
 | 
			
		||||
        ip: usize,
 | 
			
		||||
        symbol_address: usize,
 | 
			
		||||
        module_base_address: Option<usize>,
 | 
			
		||||
        symbols: Option<Vec<BacktraceSymbol>>,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl Serialize for BacktraceFrame {
 | 
			
		||||
        fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
        where
 | 
			
		||||
            S: Serializer,
 | 
			
		||||
        {
 | 
			
		||||
            let BacktraceFrame { frame, symbols } = self;
 | 
			
		||||
            SerializedFrame {
 | 
			
		||||
                ip: frame.ip() as usize,
 | 
			
		||||
                symbol_address: frame.symbol_address() as usize,
 | 
			
		||||
                module_base_address: frame.module_base_address().map(|addr| addr as usize),
 | 
			
		||||
                symbols: symbols.clone(),
 | 
			
		||||
            }
 | 
			
		||||
            .serialize(s)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl<'a> Deserialize<'a> for BacktraceFrame {
 | 
			
		||||
        fn deserialize<D>(d: D) -> Result<Self, D::Error>
 | 
			
		||||
        where
 | 
			
		||||
            D: Deserializer<'a>,
 | 
			
		||||
        {
 | 
			
		||||
            let frame: SerializedFrame = SerializedFrame::deserialize(d)?;
 | 
			
		||||
            Ok(BacktraceFrame {
 | 
			
		||||
                frame: Frame::Deserialized {
 | 
			
		||||
                    ip: frame.ip,
 | 
			
		||||
                    symbol_address: frame.symbol_address,
 | 
			
		||||
                    module_base_address: frame.module_base_address,
 | 
			
		||||
                },
 | 
			
		||||
                symbols: frame.symbols,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_frame_conversion() {
 | 
			
		||||
        let mut frames = vec![];
 | 
			
		||||
        crate::trace(|frame| {
 | 
			
		||||
            let converted = BacktraceFrame::from(frame.clone());
 | 
			
		||||
            frames.push(converted);
 | 
			
		||||
            true
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let mut manual = Backtrace::from(frames);
 | 
			
		||||
        manual.resolve();
 | 
			
		||||
        let frames = manual.frames();
 | 
			
		||||
 | 
			
		||||
        for frame in frames {
 | 
			
		||||
            println!("{:?}", frame.ip());
 | 
			
		||||
            println!("{:?}", frame.symbol_address());
 | 
			
		||||
            println!("{:?}", frame.module_base_address());
 | 
			
		||||
            println!("{:?}", frame.symbols());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										365
									
								
								vendor/backtrace/src/dbghelp.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								vendor/backtrace/src/dbghelp.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
			
		||||
//! A module to assist in managing dbghelp bindings on Windows
 | 
			
		||||
//!
 | 
			
		||||
//! Backtraces on Windows (at least for MSVC) are largely powered through
 | 
			
		||||
//! `dbghelp.dll` and the various functions that it contains. These functions
 | 
			
		||||
//! are currently loaded *dynamically* rather than linking to `dbghelp.dll`
 | 
			
		||||
//! statically. This is currently done by the standard library (and is in theory
 | 
			
		||||
//! required there), but is an effort to help reduce the static dll dependencies
 | 
			
		||||
//! of a library since backtraces are typically pretty optional. That being
 | 
			
		||||
//! said, `dbghelp.dll` almost always successfully loads on Windows.
 | 
			
		||||
//!
 | 
			
		||||
//! Note though that since we're loading all this support dynamically we can't
 | 
			
		||||
//! actually use the raw definitions in `winapi`, but rather we need to define
 | 
			
		||||
//! the function pointer types ourselves and use that. We don't really want to
 | 
			
		||||
//! be in the business of duplicating winapi, so we have a Cargo feature
 | 
			
		||||
//! `verify-winapi` which asserts that all bindings match those in winapi and
 | 
			
		||||
//! this feature is enabled on CI.
 | 
			
		||||
//!
 | 
			
		||||
//! Finally, you'll note here that the dll for `dbghelp.dll` is never unloaded,
 | 
			
		||||
//! and that's currently intentional. The thinking is that we can globally cache
 | 
			
		||||
//! it and use it between calls to the API, avoiding expensive loads/unloads. If
 | 
			
		||||
//! this is a problem for leak detectors or something like that we can cross the
 | 
			
		||||
//! bridge when we get there.
 | 
			
		||||
 | 
			
		||||
#![allow(non_snake_case)]
 | 
			
		||||
 | 
			
		||||
use super::windows::*;
 | 
			
		||||
use core::mem;
 | 
			
		||||
use core::ptr;
 | 
			
		||||
 | 
			
		||||
// Work around `SymGetOptions` and `SymSetOptions` not being present in winapi
 | 
			
		||||
// itself. Otherwise this is only used when we're double-checking types against
 | 
			
		||||
// winapi.
 | 
			
		||||
#[cfg(feature = "verify-winapi")]
 | 
			
		||||
mod dbghelp {
 | 
			
		||||
    use crate::windows::*;
 | 
			
		||||
    pub use winapi::um::dbghelp::{
 | 
			
		||||
        StackWalk64, StackWalkEx, SymCleanup, SymFromAddrW, SymFunctionTableAccess64,
 | 
			
		||||
        SymGetLineFromAddrW64, SymGetModuleBase64, SymGetOptions, SymInitializeW, SymSetOptions,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    extern "system" {
 | 
			
		||||
        // Not defined in winapi yet
 | 
			
		||||
        pub fn SymFromInlineContextW(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            Address: DWORD64,
 | 
			
		||||
            InlineContext: ULONG,
 | 
			
		||||
            Displacement: PDWORD64,
 | 
			
		||||
            Symbol: PSYMBOL_INFOW,
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        pub fn SymGetLineFromInlineContextW(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            dwAddr: DWORD64,
 | 
			
		||||
            InlineContext: ULONG,
 | 
			
		||||
            qwModuleBaseAddress: DWORD64,
 | 
			
		||||
            pdwDisplacement: PDWORD,
 | 
			
		||||
            Line: PIMAGEHLP_LINEW64,
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn assert_equal_types<T>(a: T, _b: T) -> T {
 | 
			
		||||
        a
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This macro is used to define a `Dbghelp` structure which internally contains
 | 
			
		||||
// all the function pointers that we might load.
 | 
			
		||||
macro_rules! dbghelp {
 | 
			
		||||
    (extern "system" {
 | 
			
		||||
        $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)*
 | 
			
		||||
    }) => (
 | 
			
		||||
        pub struct Dbghelp {
 | 
			
		||||
            /// The loaded DLL for `dbghelp.dll`
 | 
			
		||||
            dll: HMODULE,
 | 
			
		||||
 | 
			
		||||
            // Each function pointer for each function we might use
 | 
			
		||||
            $($name: usize,)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static mut DBGHELP: Dbghelp = Dbghelp {
 | 
			
		||||
            // Initially we haven't loaded the DLL
 | 
			
		||||
            dll: 0 as *mut _,
 | 
			
		||||
            // Initially all functions are set to zero to say they need to be
 | 
			
		||||
            // dynamically loaded.
 | 
			
		||||
            $($name: 0,)*
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Convenience typedef for each function type.
 | 
			
		||||
        $(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)*
 | 
			
		||||
 | 
			
		||||
        impl Dbghelp {
 | 
			
		||||
            /// Attempts to open `dbghelp.dll`. Returns success if it works or
 | 
			
		||||
            /// error if `LoadLibraryW` fails.
 | 
			
		||||
            ///
 | 
			
		||||
            /// Panics if library is already loaded.
 | 
			
		||||
            fn ensure_open(&mut self) -> Result<(), ()> {
 | 
			
		||||
                if !self.dll.is_null() {
 | 
			
		||||
                    return Ok(())
 | 
			
		||||
                }
 | 
			
		||||
                let lib = b"dbghelp.dll\0";
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    self.dll = LoadLibraryA(lib.as_ptr() as *const i8);
 | 
			
		||||
                    if self.dll.is_null() {
 | 
			
		||||
                        Err(())
 | 
			
		||||
                    }  else {
 | 
			
		||||
                        Ok(())
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Function for each method we'd like to use. When called it will
 | 
			
		||||
            // either read the cached function pointer or load it and return the
 | 
			
		||||
            // loaded value. Loads are asserted to succeed.
 | 
			
		||||
            $(pub fn $name(&mut self) -> Option<$name> {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    if self.$name == 0 {
 | 
			
		||||
                        let name = concat!(stringify!($name), "\0");
 | 
			
		||||
                        self.$name = self.symbol(name.as_bytes())?;
 | 
			
		||||
                    }
 | 
			
		||||
                    let ret = mem::transmute::<usize, $name>(self.$name);
 | 
			
		||||
                    #[cfg(feature = "verify-winapi")]
 | 
			
		||||
                    dbghelp::assert_equal_types(ret, dbghelp::$name);
 | 
			
		||||
                    Some(ret)
 | 
			
		||||
                }
 | 
			
		||||
            })*
 | 
			
		||||
 | 
			
		||||
            fn symbol(&self, symbol: &[u8]) -> Option<usize> {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    match GetProcAddress(self.dll, symbol.as_ptr() as *const _) as usize {
 | 
			
		||||
                        0 => None,
 | 
			
		||||
                        n => Some(n),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Convenience proxy to use the cleanup locks to reference dbghelp
 | 
			
		||||
        // functions.
 | 
			
		||||
        #[allow(dead_code)]
 | 
			
		||||
        impl Init {
 | 
			
		||||
            $(pub fn $name(&self) -> $name {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    DBGHELP.$name().unwrap()
 | 
			
		||||
                }
 | 
			
		||||
            })*
 | 
			
		||||
 | 
			
		||||
            pub fn dbghelp(&self) -> *mut Dbghelp {
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    &mut DBGHELP
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SYMOPT_DEFERRED_LOADS: DWORD = 0x00000004;
 | 
			
		||||
 | 
			
		||||
dbghelp! {
 | 
			
		||||
    extern "system" {
 | 
			
		||||
        fn SymGetOptions() -> DWORD;
 | 
			
		||||
        fn SymSetOptions(options: DWORD) -> DWORD;
 | 
			
		||||
        fn SymInitializeW(
 | 
			
		||||
            handle: HANDLE,
 | 
			
		||||
            path: PCWSTR,
 | 
			
		||||
            invade: BOOL
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        fn SymCleanup(handle: HANDLE) -> BOOL;
 | 
			
		||||
        fn StackWalk64(
 | 
			
		||||
            MachineType: DWORD,
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            hThread: HANDLE,
 | 
			
		||||
            StackFrame: LPSTACKFRAME64,
 | 
			
		||||
            ContextRecord: PVOID,
 | 
			
		||||
            ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
 | 
			
		||||
            FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
 | 
			
		||||
            GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
 | 
			
		||||
            TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        fn SymFunctionTableAccess64(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            AddrBase: DWORD64
 | 
			
		||||
        ) -> PVOID;
 | 
			
		||||
        fn SymGetModuleBase64(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            AddrBase: DWORD64
 | 
			
		||||
        ) -> DWORD64;
 | 
			
		||||
        fn SymFromAddrW(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            Address: DWORD64,
 | 
			
		||||
            Displacement: PDWORD64,
 | 
			
		||||
            Symbol: PSYMBOL_INFOW
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        fn SymGetLineFromAddrW64(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            dwAddr: DWORD64,
 | 
			
		||||
            pdwDisplacement: PDWORD,
 | 
			
		||||
            Line: PIMAGEHLP_LINEW64
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        fn StackWalkEx(
 | 
			
		||||
            MachineType: DWORD,
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            hThread: HANDLE,
 | 
			
		||||
            StackFrame: LPSTACKFRAME_EX,
 | 
			
		||||
            ContextRecord: PVOID,
 | 
			
		||||
            ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64,
 | 
			
		||||
            FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64,
 | 
			
		||||
            GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64,
 | 
			
		||||
            TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64,
 | 
			
		||||
            Flags: DWORD
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        fn SymFromInlineContextW(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            Address: DWORD64,
 | 
			
		||||
            InlineContext: ULONG,
 | 
			
		||||
            Displacement: PDWORD64,
 | 
			
		||||
            Symbol: PSYMBOL_INFOW
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        fn SymGetLineFromInlineContextW(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            dwAddr: DWORD64,
 | 
			
		||||
            InlineContext: ULONG,
 | 
			
		||||
            qwModuleBaseAddress: DWORD64,
 | 
			
		||||
            pdwDisplacement: PDWORD,
 | 
			
		||||
            Line: PIMAGEHLP_LINEW64
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Init {
 | 
			
		||||
    lock: HANDLE,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Initialize all support necessary to access `dbghelp` API functions from this
 | 
			
		||||
/// crate.
 | 
			
		||||
///
 | 
			
		||||
/// Note that this function is **safe**, it internally has its own
 | 
			
		||||
/// synchronization. Also note that it is safe to call this function multiple
 | 
			
		||||
/// times recursively.
 | 
			
		||||
pub fn init() -> Result<Init, ()> {
 | 
			
		||||
    use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 | 
			
		||||
 | 
			
		||||
    // Helper function for generating a name that's unique to the process.
 | 
			
		||||
    fn mutex_name() -> [u8; 33] {
 | 
			
		||||
        let mut name: [u8; 33] = *b"Local\\RustBacktraceMutex00000000\0";
 | 
			
		||||
        let mut id = unsafe { GetCurrentProcessId() };
 | 
			
		||||
        // Quick and dirty no alloc u32 to hex.
 | 
			
		||||
        let mut index = name.len() - 1;
 | 
			
		||||
        while id > 0 {
 | 
			
		||||
            name[index - 1] = match (id & 0xF) as u8 {
 | 
			
		||||
                h @ 0..=9 => b'0' + h,
 | 
			
		||||
                h => b'A' + (h - 10),
 | 
			
		||||
            };
 | 
			
		||||
            id >>= 4;
 | 
			
		||||
            index -= 1;
 | 
			
		||||
        }
 | 
			
		||||
        name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    unsafe {
 | 
			
		||||
        // First thing we need to do is to synchronize this function. This can
 | 
			
		||||
        // be called concurrently from other threads or recursively within one
 | 
			
		||||
        // thread. Note that it's trickier than that though because what we're
 | 
			
		||||
        // using here, `dbghelp`, *also* needs to be synchronized with all other
 | 
			
		||||
        // callers to `dbghelp` in this process.
 | 
			
		||||
        //
 | 
			
		||||
        // Typically there aren't really that many calls to `dbghelp` within the
 | 
			
		||||
        // same process and we can probably safely assume that we're the only
 | 
			
		||||
        // ones accessing it. There is, however, one primary other user we have
 | 
			
		||||
        // to worry about which is ironically ourselves, but in the standard
 | 
			
		||||
        // library. The Rust standard library depends on this crate for
 | 
			
		||||
        // backtrace support, and this crate also exists on crates.io. This
 | 
			
		||||
        // means that if the standard library is printing a panic backtrace it
 | 
			
		||||
        // may race with this crate coming from crates.io, causing segfaults.
 | 
			
		||||
        //
 | 
			
		||||
        // To help solve this synchronization problem we employ a
 | 
			
		||||
        // Windows-specific trick here (it is, after all, a Windows-specific
 | 
			
		||||
        // restriction about synchronization). We create a *session-local* named
 | 
			
		||||
        // mutex to protect this call. The intention here is that the standard
 | 
			
		||||
        // library and this crate don't have to share Rust-level APIs to
 | 
			
		||||
        // synchronize here but can instead work behind the scenes to make sure
 | 
			
		||||
        // they're synchronizing with one another. That way when this function
 | 
			
		||||
        // is called through the standard library or through crates.io we can be
 | 
			
		||||
        // sure that the same mutex is being acquired.
 | 
			
		||||
        //
 | 
			
		||||
        // So all of that is to say that the first thing we do here is we
 | 
			
		||||
        // atomically create a `HANDLE` which is a named mutex on Windows. We
 | 
			
		||||
        // synchronize a bit with other threads sharing this function
 | 
			
		||||
        // specifically and ensure that only one handle is created per instance
 | 
			
		||||
        // of this function. Note that the handle is never closed once it's
 | 
			
		||||
        // stored in the global.
 | 
			
		||||
        //
 | 
			
		||||
        // After we've actually go the lock we simply acquire it, and our `Init`
 | 
			
		||||
        // handle we hand out will be responsible for dropping it eventually.
 | 
			
		||||
        static LOCK: AtomicUsize = AtomicUsize::new(0);
 | 
			
		||||
        let mut lock = LOCK.load(SeqCst);
 | 
			
		||||
        if lock == 0 {
 | 
			
		||||
            let name = mutex_name();
 | 
			
		||||
            lock = CreateMutexA(ptr::null_mut(), 0, name.as_ptr().cast::<i8>()) as usize;
 | 
			
		||||
            if lock == 0 {
 | 
			
		||||
                return Err(());
 | 
			
		||||
            }
 | 
			
		||||
            if let Err(other) = LOCK.compare_exchange(0, lock, SeqCst, SeqCst) {
 | 
			
		||||
                debug_assert!(other != 0);
 | 
			
		||||
                CloseHandle(lock as HANDLE);
 | 
			
		||||
                lock = other;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        debug_assert!(lock != 0);
 | 
			
		||||
        let lock = lock as HANDLE;
 | 
			
		||||
        let r = WaitForSingleObjectEx(lock, INFINITE, FALSE);
 | 
			
		||||
        debug_assert_eq!(r, 0);
 | 
			
		||||
        let ret = Init { lock };
 | 
			
		||||
 | 
			
		||||
        // Ok, phew! Now that we're all safely synchronized, let's actually
 | 
			
		||||
        // start processing everything. First up we need to ensure that
 | 
			
		||||
        // `dbghelp.dll` is actually loaded in this process. We do this
 | 
			
		||||
        // dynamically to avoid a static dependency. This has historically been
 | 
			
		||||
        // done to work around weird linking issues and is intended at making
 | 
			
		||||
        // binaries a bit more portable since this is largely just a debugging
 | 
			
		||||
        // utility.
 | 
			
		||||
        //
 | 
			
		||||
        // Once we've opened `dbghelp.dll` we need to call some initialization
 | 
			
		||||
        // functions in it, and that's detailed more below. We only do this
 | 
			
		||||
        // once, though, so we've got a global boolean indicating whether we're
 | 
			
		||||
        // done yet or not.
 | 
			
		||||
        DBGHELP.ensure_open()?;
 | 
			
		||||
 | 
			
		||||
        static mut INITIALIZED: bool = false;
 | 
			
		||||
        if INITIALIZED {
 | 
			
		||||
            return Ok(ret);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let orig = DBGHELP.SymGetOptions().unwrap()();
 | 
			
		||||
 | 
			
		||||
        // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
 | 
			
		||||
        // according to MSVC's own docs about this: "This is the fastest, most
 | 
			
		||||
        // efficient way to use the symbol handler.", so let's do that!
 | 
			
		||||
        DBGHELP.SymSetOptions().unwrap()(orig | SYMOPT_DEFERRED_LOADS);
 | 
			
		||||
 | 
			
		||||
        // Actually initialize symbols with MSVC. Note that this can fail, but we
 | 
			
		||||
        // ignore it. There's not a ton of prior art for this per se, but LLVM
 | 
			
		||||
        // internally seems to ignore the return value here and one of the
 | 
			
		||||
        // sanitizer libraries in LLVM prints a scary warning if this fails but
 | 
			
		||||
        // basically ignores it in the long run.
 | 
			
		||||
        //
 | 
			
		||||
        // One case this comes up a lot for Rust is that the standard library and
 | 
			
		||||
        // this crate on crates.io both want to compete for `SymInitializeW`. The
 | 
			
		||||
        // standard library historically wanted to initialize then cleanup most of
 | 
			
		||||
        // the time, but now that it's using this crate it means that someone will
 | 
			
		||||
        // get to initialization first and the other will pick up that
 | 
			
		||||
        // initialization.
 | 
			
		||||
        DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE);
 | 
			
		||||
        INITIALIZED = true;
 | 
			
		||||
        Ok(ret)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Init {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let r = ReleaseMutex(self.lock);
 | 
			
		||||
            debug_assert!(r != 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										192
									
								
								vendor/backtrace/src/lib.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								vendor/backtrace/src/lib.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,192 @@
 | 
			
		||||
//! A library for acquiring a backtrace at runtime
 | 
			
		||||
//!
 | 
			
		||||
//! This library is meant to supplement the `RUST_BACKTRACE=1` support of the
 | 
			
		||||
//! standard library by allowing an acquisition of a backtrace at runtime
 | 
			
		||||
//! programmatically. The backtraces generated by this library do not need to be
 | 
			
		||||
//! parsed, for example, and expose the functionality of multiple backend
 | 
			
		||||
//! implementations.
 | 
			
		||||
//!
 | 
			
		||||
//! # Usage
 | 
			
		||||
//!
 | 
			
		||||
//! First, add this to your Cargo.toml
 | 
			
		||||
//!
 | 
			
		||||
//! ```toml
 | 
			
		||||
//! [dependencies]
 | 
			
		||||
//! backtrace = "0.3"
 | 
			
		||||
//! ```
 | 
			
		||||
//!
 | 
			
		||||
//! Next:
 | 
			
		||||
//!
 | 
			
		||||
//! ```
 | 
			
		||||
//! fn main() {
 | 
			
		||||
//! # // Unsafe here so test passes on no_std.
 | 
			
		||||
//! # #[cfg(feature = "std")] {
 | 
			
		||||
//!     backtrace::trace(|frame| {
 | 
			
		||||
//!         let ip = frame.ip();
 | 
			
		||||
//!         let symbol_address = frame.symbol_address();
 | 
			
		||||
//!
 | 
			
		||||
//!         // Resolve this instruction pointer to a symbol name
 | 
			
		||||
//!         backtrace::resolve_frame(frame, |symbol| {
 | 
			
		||||
//!             if let Some(name) = symbol.name() {
 | 
			
		||||
//!                 // ...
 | 
			
		||||
//!             }
 | 
			
		||||
//!             if let Some(filename) = symbol.filename() {
 | 
			
		||||
//!                 // ...
 | 
			
		||||
//!             }
 | 
			
		||||
//!         });
 | 
			
		||||
//!
 | 
			
		||||
//!         true // keep going to the next frame
 | 
			
		||||
//!     });
 | 
			
		||||
//! }
 | 
			
		||||
//! # }
 | 
			
		||||
//! ```
 | 
			
		||||
//!
 | 
			
		||||
//! # Backtrace accuracy
 | 
			
		||||
//!
 | 
			
		||||
//! This crate implements best-effort attempts to get the native backtrace. This
 | 
			
		||||
//! is not always guaranteed to work, and some platforms don't return any
 | 
			
		||||
//! backtrace at all. If your application requires accurate backtraces then it's
 | 
			
		||||
//! recommended to closely evaluate this crate to see whether it's suitable
 | 
			
		||||
//! for your use case on your target platforms.
 | 
			
		||||
//!
 | 
			
		||||
//! Even on supported platforms, there's a number of reasons that backtraces may
 | 
			
		||||
//! be less-than-accurate, including but not limited to:
 | 
			
		||||
//!
 | 
			
		||||
//! * Unwind information may not be available. This crate primarily implements
 | 
			
		||||
//!   backtraces by unwinding the stack, but not all functions may have
 | 
			
		||||
//!   unwinding information (e.g. DWARF unwinding information).
 | 
			
		||||
//!
 | 
			
		||||
//! * Rust code may be compiled without unwinding information for some
 | 
			
		||||
//!   functions. This can also happen for Rust code compiled with
 | 
			
		||||
//!   `-Cpanic=abort`. You can remedy this, however, with
 | 
			
		||||
//!   `-Cforce-unwind-tables` as a compiler option.
 | 
			
		||||
//!
 | 
			
		||||
//! * Unwind information may be inaccurate or corrupt. In the worst case
 | 
			
		||||
//!   inaccurate unwind information can lead this library to segfault. In the
 | 
			
		||||
//!   best case inaccurate information will result in a truncated stack trace.
 | 
			
		||||
//!
 | 
			
		||||
//! * Backtraces may not report filenames/line numbers correctly due to missing
 | 
			
		||||
//!   or corrupt debug information. This won't lead to segfaults unlike corrupt
 | 
			
		||||
//!   unwinding information, but missing or malformed debug information will
 | 
			
		||||
//!   mean that filenames and line numbers will not be available. This may be
 | 
			
		||||
//!   because debug information wasn't generated by the compiler, or it's just
 | 
			
		||||
//!   missing on the filesystem.
 | 
			
		||||
//!
 | 
			
		||||
//! * Not all platforms are supported. For example there's no way to get a
 | 
			
		||||
//!   backtrace on WebAssembly at the moment.
 | 
			
		||||
//!
 | 
			
		||||
//! * Crate features may be disabled. Currently this crate supports using Gimli
 | 
			
		||||
//!   libbacktrace on non-Windows platforms for reading debuginfo for
 | 
			
		||||
//!   backtraces. If both crate features are disabled, however, then these
 | 
			
		||||
//!   platforms will generate a backtrace but be unable to generate symbols for
 | 
			
		||||
//!   it.
 | 
			
		||||
//!
 | 
			
		||||
//! In most standard workflows for most standard platforms you generally don't
 | 
			
		||||
//! need to worry about these caveats. We'll try to fix ones where we can over
 | 
			
		||||
//! time, but otherwise it's important to be aware of the limitations of
 | 
			
		||||
//! unwinding-based backtraces!
 | 
			
		||||
 | 
			
		||||
#![deny(missing_docs)]
 | 
			
		||||
#![no_std]
 | 
			
		||||
#![cfg_attr(
 | 
			
		||||
    all(feature = "std", target_env = "sgx", target_vendor = "fortanix"),
 | 
			
		||||
    feature(sgx_platform)
 | 
			
		||||
)]
 | 
			
		||||
#![warn(rust_2018_idioms)]
 | 
			
		||||
// When we're building as part of libstd, silence all warnings since they're
 | 
			
		||||
// irrelevant as this crate is developed out-of-tree.
 | 
			
		||||
#![cfg_attr(backtrace_in_libstd, allow(warnings))]
 | 
			
		||||
#![cfg_attr(not(feature = "std"), allow(dead_code))]
 | 
			
		||||
// We know this is deprecated, it's only here for back-compat reasons.
 | 
			
		||||
#![cfg_attr(feature = "rustc-serialize", allow(deprecated))]
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate std;
 | 
			
		||||
 | 
			
		||||
// This is only used for gimli right now, which is only used on some platforms, and miri
 | 
			
		||||
// so don't worry if it's unused in other configurations.
 | 
			
		||||
#[allow(unused_extern_crates)]
 | 
			
		||||
extern crate alloc;
 | 
			
		||||
 | 
			
		||||
pub use self::backtrace::{trace_unsynchronized, Frame};
 | 
			
		||||
mod backtrace;
 | 
			
		||||
 | 
			
		||||
pub use self::symbolize::resolve_frame_unsynchronized;
 | 
			
		||||
pub use self::symbolize::{resolve_unsynchronized, Symbol, SymbolName};
 | 
			
		||||
mod symbolize;
 | 
			
		||||
 | 
			
		||||
pub use self::types::BytesOrWideString;
 | 
			
		||||
mod types;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub use self::symbolize::clear_symbol_cache;
 | 
			
		||||
 | 
			
		||||
mod print;
 | 
			
		||||
pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt};
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(feature = "std")] {
 | 
			
		||||
        pub use self::backtrace::trace;
 | 
			
		||||
        pub use self::symbolize::{resolve, resolve_frame};
 | 
			
		||||
        pub use self::capture::{Backtrace, BacktraceFrame, BacktraceSymbol};
 | 
			
		||||
        mod capture;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
struct Bomb {
 | 
			
		||||
    enabled: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl Drop for Bomb {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        if self.enabled {
 | 
			
		||||
            panic!("cannot panic during the backtrace function");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
mod lock {
 | 
			
		||||
    use std::boxed::Box;
 | 
			
		||||
    use std::cell::Cell;
 | 
			
		||||
    use std::sync::{Mutex, MutexGuard, Once};
 | 
			
		||||
 | 
			
		||||
    pub struct LockGuard(Option<MutexGuard<'static, ()>>);
 | 
			
		||||
 | 
			
		||||
    static mut LOCK: *mut Mutex<()> = 0 as *mut _;
 | 
			
		||||
    static INIT: Once = Once::new();
 | 
			
		||||
    thread_local!(static LOCK_HELD: Cell<bool> = Cell::new(false));
 | 
			
		||||
 | 
			
		||||
    impl Drop for LockGuard {
 | 
			
		||||
        fn drop(&mut self) {
 | 
			
		||||
            if self.0.is_some() {
 | 
			
		||||
                LOCK_HELD.with(|slot| {
 | 
			
		||||
                    assert!(slot.get());
 | 
			
		||||
                    slot.set(false);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lock() -> LockGuard {
 | 
			
		||||
        if LOCK_HELD.with(|l| l.get()) {
 | 
			
		||||
            return LockGuard(None);
 | 
			
		||||
        }
 | 
			
		||||
        LOCK_HELD.with(|s| s.set(true));
 | 
			
		||||
        unsafe {
 | 
			
		||||
            INIT.call_once(|| {
 | 
			
		||||
                LOCK = Box::into_raw(Box::new(Mutex::new(())));
 | 
			
		||||
            });
 | 
			
		||||
            LockGuard(Some((*LOCK).lock().unwrap()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(all(windows, not(target_vendor = "uwp")))]
 | 
			
		||||
mod dbghelp;
 | 
			
		||||
#[cfg(windows)]
 | 
			
		||||
mod windows;
 | 
			
		||||
							
								
								
									
										320
									
								
								vendor/backtrace/src/print.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								vendor/backtrace/src/print.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,320 @@
 | 
			
		||||
#[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 {
 | 
			
		||||
        #[cfg(target_os = "fuchsia")]
 | 
			
		||||
        fuchsia::finish_context(self.fmt)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Inserts a message in the backtrace output.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This allows information to be inserted between frames,
 | 
			
		||||
    /// and won't increment the `frame_index` unlike the `frame`
 | 
			
		||||
    /// method.
 | 
			
		||||
    pub fn message(&mut self, msg: &str) -> fmt::Result {
 | 
			
		||||
        self.fmt.write_str(msg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the inner formatter.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is used for writing custom information between frames with `write!` and `writeln!`,
 | 
			
		||||
    /// and won't increment the `frame_index` unlike the `frame` method.
 | 
			
		||||
    pub fn formatter(&mut self) -> &mut fmt::Formatter<'b> {
 | 
			
		||||
        self.fmt
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 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 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										441
									
								
								vendor/backtrace/src/print/fuchsia.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								vendor/backtrace/src/print/fuchsia.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,441 @@
 | 
			
		||||
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")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										218
									
								
								vendor/backtrace/src/symbolize/dbghelp.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								vendor/backtrace/src/symbolize/dbghelp.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
			
		||||
//! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC
 | 
			
		||||
//!
 | 
			
		||||
//! This symbolication strategy, like with backtraces, uses dynamically loaded
 | 
			
		||||
//! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why
 | 
			
		||||
//! it's dynamically loaded).
 | 
			
		||||
//!
 | 
			
		||||
//! This API selects its resolution strategy based on the frame provided or the
 | 
			
		||||
//! information we have at hand. If a frame from `StackWalkEx` is given to us
 | 
			
		||||
//! then we use similar APIs to generate correct information about inlined
 | 
			
		||||
//! functions. Otherwise if all we have is an address or an older stack frame
 | 
			
		||||
//! from `StackWalk64` we use the older APIs for symbolication.
 | 
			
		||||
//!
 | 
			
		||||
//! There's a good deal of support in this module, but a good chunk of it is
 | 
			
		||||
//! converting back and forth between Windows types and Rust types. For example
 | 
			
		||||
//! symbols come to us as wide strings which we then convert to utf-8 strings if
 | 
			
		||||
//! we can.
 | 
			
		||||
 | 
			
		||||
#![allow(bad_style)]
 | 
			
		||||
 | 
			
		||||
use super::super::{backtrace::StackFrame, dbghelp, windows::*};
 | 
			
		||||
use super::{BytesOrWideString, ResolveWhat, SymbolName};
 | 
			
		||||
use core::char;
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
use core::marker;
 | 
			
		||||
use core::mem;
 | 
			
		||||
use core::slice;
 | 
			
		||||
 | 
			
		||||
// Store an OsString on std so we can provide the symbol name and filename.
 | 
			
		||||
pub struct Symbol<'a> {
 | 
			
		||||
    name: *const [u8],
 | 
			
		||||
    addr: *mut c_void,
 | 
			
		||||
    line: Option<u32>,
 | 
			
		||||
    filename: Option<*const [u16]>,
 | 
			
		||||
    #[cfg(feature = "std")]
 | 
			
		||||
    _filename_cache: Option<::std::ffi::OsString>,
 | 
			
		||||
    #[cfg(not(feature = "std"))]
 | 
			
		||||
    _filename_cache: (),
 | 
			
		||||
    _marker: marker::PhantomData<&'a i32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Symbol<'_> {
 | 
			
		||||
    pub fn name(&self) -> Option<SymbolName<'_>> {
 | 
			
		||||
        Some(SymbolName::new(unsafe { &*self.name }))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn addr(&self) -> Option<*mut c_void> {
 | 
			
		||||
        Some(self.addr as *mut _)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
 | 
			
		||||
        self.filename
 | 
			
		||||
            .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn colno(&self) -> Option<u32> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lineno(&self) -> Option<u32> {
 | 
			
		||||
        self.line
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "std")]
 | 
			
		||||
    pub fn filename(&self) -> Option<&::std::path::Path> {
 | 
			
		||||
        use std::path::Path;
 | 
			
		||||
 | 
			
		||||
        self._filename_cache.as_ref().map(Path::new)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[repr(C, align(8))]
 | 
			
		||||
struct Aligned8<T>(T);
 | 
			
		||||
 | 
			
		||||
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
 | 
			
		||||
    // Ensure this process's symbols are initialized
 | 
			
		||||
    let dbghelp = match dbghelp::init() {
 | 
			
		||||
        Ok(dbghelp) => dbghelp,
 | 
			
		||||
        Err(()) => return, // oh well...
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    match what {
 | 
			
		||||
        ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb),
 | 
			
		||||
        ResolveWhat::Frame(frame) => match &frame.inner.stack_frame {
 | 
			
		||||
            StackFrame::New(frame) => resolve_with_inline(&dbghelp, frame, cb),
 | 
			
		||||
            StackFrame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb),
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn resolve_with_inline(
 | 
			
		||||
    dbghelp: &dbghelp::Init,
 | 
			
		||||
    frame: &STACKFRAME_EX,
 | 
			
		||||
    cb: &mut dyn FnMut(&super::Symbol),
 | 
			
		||||
) {
 | 
			
		||||
    do_resolve(
 | 
			
		||||
        |info| {
 | 
			
		||||
            dbghelp.SymFromInlineContextW()(
 | 
			
		||||
                GetCurrentProcess(),
 | 
			
		||||
                super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
 | 
			
		||||
                frame.InlineFrameContext,
 | 
			
		||||
                &mut 0,
 | 
			
		||||
                info,
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        |line| {
 | 
			
		||||
            dbghelp.SymGetLineFromInlineContextW()(
 | 
			
		||||
                GetCurrentProcess(),
 | 
			
		||||
                super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64,
 | 
			
		||||
                frame.InlineFrameContext,
 | 
			
		||||
                0,
 | 
			
		||||
                &mut 0,
 | 
			
		||||
                line,
 | 
			
		||||
            )
 | 
			
		||||
        },
 | 
			
		||||
        cb,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn resolve_without_inline(
 | 
			
		||||
    dbghelp: &dbghelp::Init,
 | 
			
		||||
    addr: *mut c_void,
 | 
			
		||||
    cb: &mut dyn FnMut(&super::Symbol),
 | 
			
		||||
) {
 | 
			
		||||
    do_resolve(
 | 
			
		||||
        |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr as DWORD64, &mut 0, info),
 | 
			
		||||
        |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr as DWORD64, &mut 0, line),
 | 
			
		||||
        cb,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn do_resolve(
 | 
			
		||||
    sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL,
 | 
			
		||||
    get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL,
 | 
			
		||||
    cb: &mut dyn FnMut(&super::Symbol),
 | 
			
		||||
) {
 | 
			
		||||
    const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>();
 | 
			
		||||
    let mut data = Aligned8([0u8; SIZE]);
 | 
			
		||||
    let data = &mut data.0;
 | 
			
		||||
    let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW);
 | 
			
		||||
    info.MaxNameLen = MAX_SYM_NAME as ULONG;
 | 
			
		||||
    // the struct size in C.  the value is different to
 | 
			
		||||
    // `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81)
 | 
			
		||||
    // due to struct alignment.
 | 
			
		||||
    info.SizeOfStruct = 88;
 | 
			
		||||
 | 
			
		||||
    if sym_from_addr(info) != TRUE {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the symbol name is greater than MaxNameLen, SymFromAddrW will
 | 
			
		||||
    // give a buffer of (MaxNameLen - 1) characters and set NameLen to
 | 
			
		||||
    // the real value.
 | 
			
		||||
    let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1);
 | 
			
		||||
    let name_ptr = info.Name.as_ptr() as *const u16;
 | 
			
		||||
    let name = slice::from_raw_parts(name_ptr, name_len);
 | 
			
		||||
 | 
			
		||||
    // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like
 | 
			
		||||
    // all other platforms
 | 
			
		||||
    let mut name_len = 0;
 | 
			
		||||
    let mut name_buffer = [0; 256];
 | 
			
		||||
    {
 | 
			
		||||
        let mut remaining = &mut name_buffer[..];
 | 
			
		||||
        for c in char::decode_utf16(name.iter().cloned()) {
 | 
			
		||||
            let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);
 | 
			
		||||
            let len = c.len_utf8();
 | 
			
		||||
            if len < remaining.len() {
 | 
			
		||||
                c.encode_utf8(remaining);
 | 
			
		||||
                let tmp = remaining;
 | 
			
		||||
                remaining = &mut tmp[len..];
 | 
			
		||||
                name_len += len;
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let name = &name_buffer[..name_len] as *const [u8];
 | 
			
		||||
 | 
			
		||||
    let mut line = mem::zeroed::<IMAGEHLP_LINEW64>();
 | 
			
		||||
    line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD;
 | 
			
		||||
 | 
			
		||||
    let mut filename = None;
 | 
			
		||||
    let mut lineno = None;
 | 
			
		||||
    if get_line_from_addr(&mut line) == TRUE {
 | 
			
		||||
        lineno = Some(line.LineNumber as u32);
 | 
			
		||||
 | 
			
		||||
        let base = line.FileName;
 | 
			
		||||
        let mut len = 0;
 | 
			
		||||
        while *base.offset(len) != 0 {
 | 
			
		||||
            len += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let len = len as usize;
 | 
			
		||||
 | 
			
		||||
        filename = Some(slice::from_raw_parts(base, len) as *const [u16]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cb(&super::Symbol {
 | 
			
		||||
        inner: Symbol {
 | 
			
		||||
            name,
 | 
			
		||||
            addr: info.Address as *mut _,
 | 
			
		||||
            line: lineno,
 | 
			
		||||
            filename,
 | 
			
		||||
            _filename_cache: cache(filename),
 | 
			
		||||
            _marker: marker::PhantomData,
 | 
			
		||||
        },
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
 | 
			
		||||
    use std::os::windows::ffi::OsStringExt;
 | 
			
		||||
    filename.map(|f| ::std::ffi::OsString::from_wide(&*f))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(not(feature = "std"))]
 | 
			
		||||
unsafe fn cache(_filename: Option<*const [u16]>) {}
 | 
			
		||||
 | 
			
		||||
pub unsafe fn clear_symbol_cache() {}
 | 
			
		||||
							
								
								
									
										511
									
								
								vendor/backtrace/src/symbolize/gimli.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										511
									
								
								vendor/backtrace/src/symbolize/gimli.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,511 @@
 | 
			
		||||
//! Support for symbolication using the `gimli` crate on crates.io
 | 
			
		||||
//!
 | 
			
		||||
//! This is the default symbolication implementation for Rust.
 | 
			
		||||
 | 
			
		||||
use self::gimli::read::EndianSlice;
 | 
			
		||||
use self::gimli::NativeEndian as Endian;
 | 
			
		||||
use self::mmap::Mmap;
 | 
			
		||||
use self::stash::Stash;
 | 
			
		||||
use super::BytesOrWideString;
 | 
			
		||||
use super::ResolveWhat;
 | 
			
		||||
use super::SymbolName;
 | 
			
		||||
use addr2line::gimli;
 | 
			
		||||
use core::convert::TryInto;
 | 
			
		||||
use core::mem;
 | 
			
		||||
use core::u32;
 | 
			
		||||
use libc::c_void;
 | 
			
		||||
use mystd::ffi::OsString;
 | 
			
		||||
use mystd::fs::File;
 | 
			
		||||
use mystd::path::Path;
 | 
			
		||||
use mystd::prelude::v1::*;
 | 
			
		||||
 | 
			
		||||
#[cfg(backtrace_in_libstd)]
 | 
			
		||||
mod mystd {
 | 
			
		||||
    pub use crate::*;
 | 
			
		||||
}
 | 
			
		||||
#[cfg(not(backtrace_in_libstd))]
 | 
			
		||||
extern crate std as mystd;
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(windows)] {
 | 
			
		||||
        #[path = "gimli/mmap_windows.rs"]
 | 
			
		||||
        mod mmap;
 | 
			
		||||
    } else if #[cfg(any(
 | 
			
		||||
        target_os = "android",
 | 
			
		||||
        target_os = "freebsd",
 | 
			
		||||
        target_os = "fuchsia",
 | 
			
		||||
        target_os = "haiku",
 | 
			
		||||
        target_os = "ios",
 | 
			
		||||
        target_os = "linux",
 | 
			
		||||
        target_os = "macos",
 | 
			
		||||
        target_os = "openbsd",
 | 
			
		||||
        target_os = "solaris",
 | 
			
		||||
        target_os = "illumos",
 | 
			
		||||
    ))] {
 | 
			
		||||
        #[path = "gimli/mmap_unix.rs"]
 | 
			
		||||
        mod mmap;
 | 
			
		||||
    } else {
 | 
			
		||||
        #[path = "gimli/mmap_fake.rs"]
 | 
			
		||||
        mod mmap;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mod stash;
 | 
			
		||||
 | 
			
		||||
const MAPPINGS_CACHE_SIZE: usize = 4;
 | 
			
		||||
 | 
			
		||||
struct Mapping {
 | 
			
		||||
    // 'static lifetime is a lie to hack around lack of support for self-referential structs.
 | 
			
		||||
    cx: Context<'static>,
 | 
			
		||||
    _map: Mmap,
 | 
			
		||||
    stash: Stash,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum Either<A, B> {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    A(A),
 | 
			
		||||
    B(B),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mapping {
 | 
			
		||||
    /// Creates a `Mapping` by ensuring that the `data` specified is used to
 | 
			
		||||
    /// create a `Context` and it can only borrow from that or the `Stash` of
 | 
			
		||||
    /// decompressed sections or auxiliary data.
 | 
			
		||||
    fn mk<F>(data: Mmap, mk: F) -> Option<Mapping>
 | 
			
		||||
    where
 | 
			
		||||
        F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Context<'a>>,
 | 
			
		||||
    {
 | 
			
		||||
        Mapping::mk_or_other(data, move |data, stash| {
 | 
			
		||||
            let cx = mk(data, stash)?;
 | 
			
		||||
            Some(Either::B(cx))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Creates a `Mapping` from `data`, or if the closure decides to, returns a
 | 
			
		||||
    /// different mapping.
 | 
			
		||||
    fn mk_or_other<F>(data: Mmap, mk: F) -> Option<Mapping>
 | 
			
		||||
    where
 | 
			
		||||
        F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Either<Mapping, Context<'a>>>,
 | 
			
		||||
    {
 | 
			
		||||
        let stash = Stash::new();
 | 
			
		||||
        let cx = match mk(&data, &stash)? {
 | 
			
		||||
            Either::A(mapping) => return Some(mapping),
 | 
			
		||||
            Either::B(cx) => cx,
 | 
			
		||||
        };
 | 
			
		||||
        Some(Mapping {
 | 
			
		||||
            // Convert to 'static lifetimes since the symbols should
 | 
			
		||||
            // only borrow `map` and `stash` and we're preserving them below.
 | 
			
		||||
            cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
 | 
			
		||||
            _map: data,
 | 
			
		||||
            stash: stash,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Context<'a> {
 | 
			
		||||
    dwarf: addr2line::Context<EndianSlice<'a, Endian>>,
 | 
			
		||||
    object: Object<'a>,
 | 
			
		||||
    package: Option<gimli::DwarfPackage<EndianSlice<'a, Endian>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'data> Context<'data> {
 | 
			
		||||
    fn new(
 | 
			
		||||
        stash: &'data Stash,
 | 
			
		||||
        object: Object<'data>,
 | 
			
		||||
        sup: Option<Object<'data>>,
 | 
			
		||||
        dwp: Option<Object<'data>>,
 | 
			
		||||
    ) -> Option<Context<'data>> {
 | 
			
		||||
        let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> {
 | 
			
		||||
            let data = object.section(stash, id.name()).unwrap_or(&[]);
 | 
			
		||||
            Ok(EndianSlice::new(data, Endian))
 | 
			
		||||
        })
 | 
			
		||||
        .ok()?;
 | 
			
		||||
 | 
			
		||||
        if let Some(sup) = sup {
 | 
			
		||||
            sections
 | 
			
		||||
                .load_sup(|id| -> Result<_, ()> {
 | 
			
		||||
                    let data = sup.section(stash, id.name()).unwrap_or(&[]);
 | 
			
		||||
                    Ok(EndianSlice::new(data, Endian))
 | 
			
		||||
                })
 | 
			
		||||
                .ok()?;
 | 
			
		||||
        }
 | 
			
		||||
        let dwarf = addr2line::Context::from_dwarf(sections).ok()?;
 | 
			
		||||
 | 
			
		||||
        let mut package = None;
 | 
			
		||||
        if let Some(dwp) = dwp {
 | 
			
		||||
            package = Some(
 | 
			
		||||
                gimli::DwarfPackage::load(
 | 
			
		||||
                    |id| -> Result<_, gimli::Error> {
 | 
			
		||||
                        let data = id
 | 
			
		||||
                            .dwo_name()
 | 
			
		||||
                            .and_then(|name| dwp.section(stash, name))
 | 
			
		||||
                            .unwrap_or(&[]);
 | 
			
		||||
                        Ok(EndianSlice::new(data, Endian))
 | 
			
		||||
                    },
 | 
			
		||||
                    EndianSlice::new(&[], Endian),
 | 
			
		||||
                )
 | 
			
		||||
                .ok()?,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Some(Context {
 | 
			
		||||
            dwarf,
 | 
			
		||||
            object,
 | 
			
		||||
            package,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn find_frames(
 | 
			
		||||
        &'_ self,
 | 
			
		||||
        stash: &'data Stash,
 | 
			
		||||
        probe: u64,
 | 
			
		||||
    ) -> gimli::Result<addr2line::FrameIter<'_, EndianSlice<'data, Endian>>> {
 | 
			
		||||
        use addr2line::{LookupContinuation, LookupResult};
 | 
			
		||||
 | 
			
		||||
        let mut l = self.dwarf.find_frames(probe);
 | 
			
		||||
        loop {
 | 
			
		||||
            let (load, continuation) = match l {
 | 
			
		||||
                LookupResult::Output(output) => break output,
 | 
			
		||||
                LookupResult::Load { load, continuation } => (load, continuation),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            l = continuation.resume(handle_split_dwarf(self.package.as_ref(), stash, load));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn mmap(path: &Path) -> Option<Mmap> {
 | 
			
		||||
    let file = File::open(path).ok()?;
 | 
			
		||||
    let len = file.metadata().ok()?.len().try_into().ok()?;
 | 
			
		||||
    unsafe { Mmap::map(&file, len) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(windows)] {
 | 
			
		||||
        mod coff;
 | 
			
		||||
        use self::coff::{handle_split_dwarf, Object};
 | 
			
		||||
    } else if #[cfg(any(
 | 
			
		||||
        target_os = "macos",
 | 
			
		||||
        target_os = "ios",
 | 
			
		||||
        target_os = "tvos",
 | 
			
		||||
        target_os = "watchos",
 | 
			
		||||
    ))] {
 | 
			
		||||
        mod macho;
 | 
			
		||||
        use self::macho::{handle_split_dwarf, Object};
 | 
			
		||||
    } else {
 | 
			
		||||
        mod elf;
 | 
			
		||||
        use self::elf::{handle_split_dwarf, Object};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(windows)] {
 | 
			
		||||
        mod libs_windows;
 | 
			
		||||
        use libs_windows::native_libraries;
 | 
			
		||||
    } else if #[cfg(any(
 | 
			
		||||
        target_os = "macos",
 | 
			
		||||
        target_os = "ios",
 | 
			
		||||
        target_os = "tvos",
 | 
			
		||||
        target_os = "watchos",
 | 
			
		||||
    ))] {
 | 
			
		||||
        mod libs_macos;
 | 
			
		||||
        use libs_macos::native_libraries;
 | 
			
		||||
    } else if #[cfg(target_os = "illumos")] {
 | 
			
		||||
        mod libs_illumos;
 | 
			
		||||
        use libs_illumos::native_libraries;
 | 
			
		||||
    } else if #[cfg(all(
 | 
			
		||||
        any(
 | 
			
		||||
            target_os = "linux",
 | 
			
		||||
            target_os = "fuchsia",
 | 
			
		||||
            target_os = "freebsd",
 | 
			
		||||
            target_os = "openbsd",
 | 
			
		||||
            target_os = "netbsd",
 | 
			
		||||
            all(target_os = "android", feature = "dl_iterate_phdr"),
 | 
			
		||||
        ),
 | 
			
		||||
        not(target_env = "uclibc"),
 | 
			
		||||
    ))] {
 | 
			
		||||
        mod libs_dl_iterate_phdr;
 | 
			
		||||
        use libs_dl_iterate_phdr::native_libraries;
 | 
			
		||||
        #[path = "gimli/parse_running_mmaps_unix.rs"]
 | 
			
		||||
        mod parse_running_mmaps;
 | 
			
		||||
    } else if #[cfg(target_env = "libnx")] {
 | 
			
		||||
        mod libs_libnx;
 | 
			
		||||
        use libs_libnx::native_libraries;
 | 
			
		||||
    } else if #[cfg(target_os = "haiku")] {
 | 
			
		||||
        mod libs_haiku;
 | 
			
		||||
        use libs_haiku::native_libraries;
 | 
			
		||||
    } else {
 | 
			
		||||
        // Everything else should doesn't know how to load native libraries.
 | 
			
		||||
        fn native_libraries() -> Vec<Library> {
 | 
			
		||||
            Vec::new()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Default)]
 | 
			
		||||
struct Cache {
 | 
			
		||||
    /// All known shared libraries that have been loaded.
 | 
			
		||||
    libraries: Vec<Library>,
 | 
			
		||||
 | 
			
		||||
    /// Mappings cache where we retain parsed dwarf information.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This list has a fixed capacity for its entire lifetime which never
 | 
			
		||||
    /// increases. The `usize` element of each pair is an index into `libraries`
 | 
			
		||||
    /// above where `usize::max_value()` represents the current executable. The
 | 
			
		||||
    /// `Mapping` is corresponding parsed dwarf information.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that this is basically an LRU cache and we'll be shifting things
 | 
			
		||||
    /// around in here as we symbolize addresses.
 | 
			
		||||
    mappings: Vec<(usize, Mapping)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Library {
 | 
			
		||||
    name: OsString,
 | 
			
		||||
    /// Segments of this library loaded into memory, and where they're loaded.
 | 
			
		||||
    segments: Vec<LibrarySegment>,
 | 
			
		||||
    /// The "bias" of this library, typically where it's loaded into memory.
 | 
			
		||||
    /// This value is added to each segment's stated address to get the actual
 | 
			
		||||
    /// virtual memory address that the segment is loaded into. Additionally
 | 
			
		||||
    /// this bias is subtracted from real virtual memory addresses to index into
 | 
			
		||||
    /// debuginfo and the symbol table.
 | 
			
		||||
    bias: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct LibrarySegment {
 | 
			
		||||
    /// The stated address of this segment in the object file. This is not
 | 
			
		||||
    /// actually where the segment is loaded, but rather this address plus the
 | 
			
		||||
    /// containing library's `bias` is where to find it.
 | 
			
		||||
    stated_virtual_memory_address: usize,
 | 
			
		||||
    /// The size of this segment in memory.
 | 
			
		||||
    len: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unsafe because this is required to be externally synchronized
 | 
			
		||||
pub unsafe fn clear_symbol_cache() {
 | 
			
		||||
    Cache::with_global(|cache| cache.mappings.clear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Cache {
 | 
			
		||||
    fn new() -> Cache {
 | 
			
		||||
        Cache {
 | 
			
		||||
            mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE),
 | 
			
		||||
            libraries: native_libraries(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // unsafe because this is required to be externally synchronized
 | 
			
		||||
    unsafe fn with_global(f: impl FnOnce(&mut Self)) {
 | 
			
		||||
        // A very small, very simple LRU cache for debug info mappings.
 | 
			
		||||
        //
 | 
			
		||||
        // The hit rate should be very high, since the typical stack doesn't cross
 | 
			
		||||
        // between many shared libraries.
 | 
			
		||||
        //
 | 
			
		||||
        // The `addr2line::Context` structures are pretty expensive to create. Its
 | 
			
		||||
        // cost is expected to be amortized by subsequent `locate` queries, which
 | 
			
		||||
        // leverage the structures built when constructing `addr2line::Context`s to
 | 
			
		||||
        // get nice speedups. If we didn't have this cache, that amortization would
 | 
			
		||||
        // never happen, and symbolicating backtraces would be ssssllllooooowwww.
 | 
			
		||||
        static mut MAPPINGS_CACHE: Option<Cache> = None;
 | 
			
		||||
 | 
			
		||||
        f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, *const u8)> {
 | 
			
		||||
        self.libraries
 | 
			
		||||
            .iter()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
            .filter_map(|(i, lib)| {
 | 
			
		||||
                // First up, test if this `lib` has any segment containing the
 | 
			
		||||
                // `addr` (handling relocation). If this check passes then we
 | 
			
		||||
                // can continue below and actually translate the address.
 | 
			
		||||
                //
 | 
			
		||||
                // Note that we're using `wrapping_add` here to avoid overflow
 | 
			
		||||
                // checks. It's been seen in the wild that the SVMA + bias
 | 
			
		||||
                // computation overflows. It seems a bit odd that would happen
 | 
			
		||||
                // but there's not a huge amount we can do about it other than
 | 
			
		||||
                // probably just ignore those segments since they're likely
 | 
			
		||||
                // pointing off into space. This originally came up in
 | 
			
		||||
                // rust-lang/backtrace-rs#329.
 | 
			
		||||
                if !lib.segments.iter().any(|s| {
 | 
			
		||||
                    let svma = s.stated_virtual_memory_address;
 | 
			
		||||
                    let start = svma.wrapping_add(lib.bias);
 | 
			
		||||
                    let end = start.wrapping_add(s.len);
 | 
			
		||||
                    let address = addr as usize;
 | 
			
		||||
                    start <= address && address < end
 | 
			
		||||
                }) {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Now that we know `lib` contains `addr`, we can offset with
 | 
			
		||||
                // the bias to find the stated virtual memory address.
 | 
			
		||||
                let svma = (addr as usize).wrapping_sub(lib.bias);
 | 
			
		||||
                Some((i, svma as *const u8))
 | 
			
		||||
            })
 | 
			
		||||
            .next()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<(&'a mut Context<'a>, &'a Stash)> {
 | 
			
		||||
        let idx = self.mappings.iter().position(|(idx, _)| *idx == lib);
 | 
			
		||||
 | 
			
		||||
        // Invariant: after this conditional completes without early returning
 | 
			
		||||
        // from an error, the cache entry for this path is at index 0.
 | 
			
		||||
 | 
			
		||||
        if let Some(idx) = idx {
 | 
			
		||||
            // When the mapping is already in the cache, move it to the front.
 | 
			
		||||
            if idx != 0 {
 | 
			
		||||
                let entry = self.mappings.remove(idx);
 | 
			
		||||
                self.mappings.insert(0, entry);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // When the mapping is not in the cache, create a new mapping,
 | 
			
		||||
            // insert it into the front of the cache, and evict the oldest cache
 | 
			
		||||
            // entry if necessary.
 | 
			
		||||
            let name = &self.libraries[lib].name;
 | 
			
		||||
            let mapping = Mapping::new(name.as_ref())?;
 | 
			
		||||
 | 
			
		||||
            if self.mappings.len() == MAPPINGS_CACHE_SIZE {
 | 
			
		||||
                self.mappings.pop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.mappings.insert(0, (lib, mapping));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mapping = &mut self.mappings[0].1;
 | 
			
		||||
        let cx: &'a mut Context<'static> = &mut mapping.cx;
 | 
			
		||||
        let stash: &'a Stash = &mapping.stash;
 | 
			
		||||
        // don't leak the `'static` lifetime, make sure it's scoped to just
 | 
			
		||||
        // ourselves
 | 
			
		||||
        Some((
 | 
			
		||||
            unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) },
 | 
			
		||||
            stash,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
 | 
			
		||||
    let addr = what.address_or_ip();
 | 
			
		||||
    let mut call = |sym: Symbol<'_>| {
 | 
			
		||||
        // Extend the lifetime of `sym` to `'static` since we are unfortunately
 | 
			
		||||
        // required to here, but it's only ever going out as a reference so no
 | 
			
		||||
        // reference to it should be persisted beyond this frame anyway.
 | 
			
		||||
        let sym = mem::transmute::<Symbol<'_>, Symbol<'static>>(sym);
 | 
			
		||||
        (cb)(&super::Symbol { inner: sym });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Cache::with_global(|cache| {
 | 
			
		||||
        let (lib, addr) = match cache.avma_to_svma(addr as *const u8) {
 | 
			
		||||
            Some(pair) => pair,
 | 
			
		||||
            None => return,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Finally, get a cached mapping or create a new mapping for this file, and
 | 
			
		||||
        // evaluate the DWARF info to find the file/line/name for this address.
 | 
			
		||||
        let (cx, stash) = match cache.mapping_for_lib(lib) {
 | 
			
		||||
            Some((cx, stash)) => (cx, stash),
 | 
			
		||||
            None => return,
 | 
			
		||||
        };
 | 
			
		||||
        let mut any_frames = false;
 | 
			
		||||
        if let Ok(mut frames) = cx.find_frames(stash, addr as u64) {
 | 
			
		||||
            while let Ok(Some(frame)) = frames.next() {
 | 
			
		||||
                any_frames = true;
 | 
			
		||||
                let name = match frame.function {
 | 
			
		||||
                    Some(f) => Some(f.name.slice()),
 | 
			
		||||
                    None => cx.object.search_symtab(addr as u64),
 | 
			
		||||
                };
 | 
			
		||||
                call(Symbol::Frame {
 | 
			
		||||
                    addr: addr as *mut c_void,
 | 
			
		||||
                    location: frame.location,
 | 
			
		||||
                    name,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if !any_frames {
 | 
			
		||||
            if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
 | 
			
		||||
                if let Ok(mut frames) = object_cx.find_frames(stash, object_addr) {
 | 
			
		||||
                    while let Ok(Some(frame)) = frames.next() {
 | 
			
		||||
                        any_frames = true;
 | 
			
		||||
                        call(Symbol::Frame {
 | 
			
		||||
                            addr: addr as *mut c_void,
 | 
			
		||||
                            location: frame.location,
 | 
			
		||||
                            name: frame.function.map(|f| f.name.slice()),
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if !any_frames {
 | 
			
		||||
            if let Some(name) = cx.object.search_symtab(addr as u64) {
 | 
			
		||||
                call(Symbol::Symtab {
 | 
			
		||||
                    addr: addr as *mut c_void,
 | 
			
		||||
                    name,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum Symbol<'a> {
 | 
			
		||||
    /// We were able to locate frame information for this symbol, and
 | 
			
		||||
    /// `addr2line`'s frame internally has all the nitty gritty details.
 | 
			
		||||
    Frame {
 | 
			
		||||
        addr: *mut c_void,
 | 
			
		||||
        location: Option<addr2line::Location<'a>>,
 | 
			
		||||
        name: Option<&'a [u8]>,
 | 
			
		||||
    },
 | 
			
		||||
    /// Couldn't find debug information, but we found it in the symbol table of
 | 
			
		||||
    /// the elf executable.
 | 
			
		||||
    Symtab { addr: *mut c_void, name: &'a [u8] },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Symbol<'_> {
 | 
			
		||||
    pub fn name(&self) -> Option<SymbolName<'_>> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Symbol::Frame { name, .. } => {
 | 
			
		||||
                let name = name.as_ref()?;
 | 
			
		||||
                Some(SymbolName::new(name))
 | 
			
		||||
            }
 | 
			
		||||
            Symbol::Symtab { name, .. } => Some(SymbolName::new(name)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn addr(&self) -> Option<*mut c_void> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Symbol::Frame { addr, .. } => Some(*addr),
 | 
			
		||||
            Symbol::Symtab { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Symbol::Frame { location, .. } => {
 | 
			
		||||
                let file = location.as_ref()?.file?;
 | 
			
		||||
                Some(BytesOrWideString::Bytes(file.as_bytes()))
 | 
			
		||||
            }
 | 
			
		||||
            Symbol::Symtab { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn filename(&self) -> Option<&Path> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Symbol::Frame { location, .. } => {
 | 
			
		||||
                let file = location.as_ref()?.file?;
 | 
			
		||||
                Some(Path::new(file))
 | 
			
		||||
            }
 | 
			
		||||
            Symbol::Symtab { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lineno(&self) -> Option<u32> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Symbol::Frame { location, .. } => location.as_ref()?.line,
 | 
			
		||||
            Symbol::Symtab { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn colno(&self) -> Option<u32> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Symbol::Frame { location, .. } => location.as_ref()?.column,
 | 
			
		||||
            Symbol::Symtab { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								vendor/backtrace/src/symbolize/gimli/coff.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								vendor/backtrace/src/symbolize/gimli/coff.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
use super::{gimli, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec};
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use core::convert::TryFrom;
 | 
			
		||||
use object::pe::{ImageDosHeader, ImageSymbol};
 | 
			
		||||
use object::read::coff::ImageSymbol as _;
 | 
			
		||||
use object::read::pe::{ImageNtHeaders, ImageOptionalHeader, SectionTable};
 | 
			
		||||
use object::read::StringTable;
 | 
			
		||||
use object::LittleEndian as LE;
 | 
			
		||||
 | 
			
		||||
#[cfg(target_pointer_width = "32")]
 | 
			
		||||
type Pe = object::pe::ImageNtHeaders32;
 | 
			
		||||
#[cfg(target_pointer_width = "64")]
 | 
			
		||||
type Pe = object::pe::ImageNtHeaders64;
 | 
			
		||||
 | 
			
		||||
impl Mapping {
 | 
			
		||||
    pub fn new(path: &Path) -> Option<Mapping> {
 | 
			
		||||
        let map = super::mmap(path)?;
 | 
			
		||||
        Mapping::mk(map, |data, stash| {
 | 
			
		||||
            Context::new(stash, Object::parse(data)?, None, None)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Object<'a> {
 | 
			
		||||
    data: &'a [u8],
 | 
			
		||||
    sections: SectionTable<'a>,
 | 
			
		||||
    symbols: Vec<(usize, &'a ImageSymbol)>,
 | 
			
		||||
    strings: StringTable<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn get_image_base(data: &[u8]) -> Option<usize> {
 | 
			
		||||
    let dos_header = ImageDosHeader::parse(data).ok()?;
 | 
			
		||||
    let mut offset = dos_header.nt_headers_offset().into();
 | 
			
		||||
    let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
 | 
			
		||||
    usize::try_from(nt_headers.optional_header().image_base()).ok()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Object<'a> {
 | 
			
		||||
    fn parse(data: &'a [u8]) -> Option<Object<'a>> {
 | 
			
		||||
        let dos_header = ImageDosHeader::parse(data).ok()?;
 | 
			
		||||
        let mut offset = dos_header.nt_headers_offset().into();
 | 
			
		||||
        let (nt_headers, _) = Pe::parse(data, &mut offset).ok()?;
 | 
			
		||||
        let sections = nt_headers.sections(data, offset).ok()?;
 | 
			
		||||
        let symtab = nt_headers.symbols(data).ok()?;
 | 
			
		||||
        let strings = symtab.strings();
 | 
			
		||||
        let image_base = usize::try_from(nt_headers.optional_header().image_base()).ok()?;
 | 
			
		||||
 | 
			
		||||
        // Collect all the symbols into a local vector which is sorted
 | 
			
		||||
        // by address and contains enough data to learn about the symbol
 | 
			
		||||
        // name. Note that we only look at function symbols and also
 | 
			
		||||
        // note that the sections are 1-indexed because the zero section
 | 
			
		||||
        // is special (apparently).
 | 
			
		||||
        let mut symbols = Vec::new();
 | 
			
		||||
        let mut i = 0;
 | 
			
		||||
        let len = symtab.len();
 | 
			
		||||
        while i < len {
 | 
			
		||||
            let sym = symtab.symbol(i).ok()?;
 | 
			
		||||
            i += 1 + sym.number_of_aux_symbols as usize;
 | 
			
		||||
            let section_number = sym.section_number.get(LE);
 | 
			
		||||
            if sym.derived_type() != object::pe::IMAGE_SYM_DTYPE_FUNCTION || section_number == 0 {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            let addr = usize::try_from(sym.value.get(LE)).ok()?;
 | 
			
		||||
            let section = sections
 | 
			
		||||
                .section(usize::try_from(section_number).ok()?)
 | 
			
		||||
                .ok()?;
 | 
			
		||||
            let va = usize::try_from(section.virtual_address.get(LE)).ok()?;
 | 
			
		||||
            symbols.push((addr + va + image_base, sym));
 | 
			
		||||
        }
 | 
			
		||||
        symbols.sort_unstable_by_key(|x| x.0);
 | 
			
		||||
        Some(Object {
 | 
			
		||||
            data,
 | 
			
		||||
            sections,
 | 
			
		||||
            strings,
 | 
			
		||||
            symbols,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
 | 
			
		||||
        Some(
 | 
			
		||||
            self.sections
 | 
			
		||||
                .section_by_name(self.strings, name.as_bytes())?
 | 
			
		||||
                .1
 | 
			
		||||
                .pe_data(self.data)
 | 
			
		||||
                .ok()?,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
 | 
			
		||||
        // Note that unlike other formats COFF doesn't embed the size of
 | 
			
		||||
        // each symbol. As a last ditch effort search for the *closest*
 | 
			
		||||
        // symbol to a particular address and return that one. This gets
 | 
			
		||||
        // really wonky once symbols start getting removed because the
 | 
			
		||||
        // symbols returned here can be totally incorrect, but we have
 | 
			
		||||
        // no idea of knowing how to detect that.
 | 
			
		||||
        let addr = usize::try_from(addr).ok()?;
 | 
			
		||||
        let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) {
 | 
			
		||||
            Ok(i) => i,
 | 
			
		||||
            // typically `addr` isn't in the array, but `i` is where
 | 
			
		||||
            // we'd insert it, so the previous position must be the
 | 
			
		||||
            // greatest less than `addr`
 | 
			
		||||
            Err(i) => i.checked_sub(1)?,
 | 
			
		||||
        };
 | 
			
		||||
        self.symbols[i].1.name(self.strings).ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(super) fn handle_split_dwarf<'data>(
 | 
			
		||||
    _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
 | 
			
		||||
    _stash: &'data Stash,
 | 
			
		||||
    _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
 | 
			
		||||
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										495
									
								
								vendor/backtrace/src/symbolize/gimli/elf.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										495
									
								
								vendor/backtrace/src/symbolize/gimli/elf.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,495 @@
 | 
			
		||||
use super::mystd::ffi::{OsStr, OsString};
 | 
			
		||||
use super::mystd::fs;
 | 
			
		||||
use super::mystd::os::unix::ffi::{OsStrExt, OsStringExt};
 | 
			
		||||
use super::mystd::path::{Path, PathBuf};
 | 
			
		||||
use super::Either;
 | 
			
		||||
use super::{gimli, Context, Endian, EndianSlice, Mapping, Stash, Vec};
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use core::convert::{TryFrom, TryInto};
 | 
			
		||||
use core::str;
 | 
			
		||||
use object::elf::{ELFCOMPRESS_ZLIB, ELF_NOTE_GNU, NT_GNU_BUILD_ID, SHF_COMPRESSED};
 | 
			
		||||
use object::read::elf::{CompressionHeader, FileHeader, SectionHeader, SectionTable, Sym};
 | 
			
		||||
use object::read::StringTable;
 | 
			
		||||
use object::{BigEndian, Bytes, NativeEndian};
 | 
			
		||||
 | 
			
		||||
#[cfg(target_pointer_width = "32")]
 | 
			
		||||
type Elf = object::elf::FileHeader32<NativeEndian>;
 | 
			
		||||
#[cfg(target_pointer_width = "64")]
 | 
			
		||||
type Elf = object::elf::FileHeader64<NativeEndian>;
 | 
			
		||||
 | 
			
		||||
impl Mapping {
 | 
			
		||||
    pub fn new(path: &Path) -> Option<Mapping> {
 | 
			
		||||
        let map = super::mmap(path)?;
 | 
			
		||||
        Mapping::mk_or_other(map, |map, stash| {
 | 
			
		||||
            let object = Object::parse(&map)?;
 | 
			
		||||
 | 
			
		||||
            // Try to locate an external debug file using the build ID.
 | 
			
		||||
            if let Some(path_debug) = object.build_id().and_then(locate_build_id) {
 | 
			
		||||
                if let Some(mapping) = Mapping::new_debug(path, path_debug, None) {
 | 
			
		||||
                    return Some(Either::A(mapping));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Try to locate an external debug file using the GNU debug link section.
 | 
			
		||||
            if let Some((path_debug, crc)) = object.gnu_debuglink_path(path) {
 | 
			
		||||
                if let Some(mapping) = Mapping::new_debug(path, path_debug, Some(crc)) {
 | 
			
		||||
                    return Some(Either::A(mapping));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let dwp = Mapping::load_dwarf_package(path, stash);
 | 
			
		||||
 | 
			
		||||
            Context::new(stash, object, None, dwp).map(Either::B)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Load debuginfo from an external debug file.
 | 
			
		||||
    fn new_debug(original_path: &Path, path: PathBuf, crc: Option<u32>) -> Option<Mapping> {
 | 
			
		||||
        let map = super::mmap(&path)?;
 | 
			
		||||
        Mapping::mk(map, |map, stash| {
 | 
			
		||||
            let object = Object::parse(&map)?;
 | 
			
		||||
 | 
			
		||||
            if let Some(_crc) = crc {
 | 
			
		||||
                // TODO: check crc
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Try to locate a supplementary object file.
 | 
			
		||||
            let mut sup = None;
 | 
			
		||||
            if let Some((path_sup, build_id_sup)) = object.gnu_debugaltlink_path(&path) {
 | 
			
		||||
                if let Some(map_sup) = super::mmap(&path_sup) {
 | 
			
		||||
                    let map_sup = stash.cache_mmap(map_sup);
 | 
			
		||||
                    if let Some(sup_) = Object::parse(map_sup) {
 | 
			
		||||
                        if sup_.build_id() == Some(build_id_sup) {
 | 
			
		||||
                            sup = Some(sup_);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let dwp = Mapping::load_dwarf_package(original_path, stash);
 | 
			
		||||
 | 
			
		||||
            Context::new(stash, object, sup, dwp)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Try to locate a DWARF package file.
 | 
			
		||||
    fn load_dwarf_package<'data>(path: &Path, stash: &'data Stash) -> Option<Object<'data>> {
 | 
			
		||||
        let mut path_dwp = path.to_path_buf();
 | 
			
		||||
        let dwp_extension = path
 | 
			
		||||
            .extension()
 | 
			
		||||
            .map(|previous_extension| {
 | 
			
		||||
                let mut previous_extension = previous_extension.to_os_string();
 | 
			
		||||
                previous_extension.push(".dwp");
 | 
			
		||||
                previous_extension
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap_or_else(|| "dwp".into());
 | 
			
		||||
        path_dwp.set_extension(dwp_extension);
 | 
			
		||||
        if let Some(map_dwp) = super::mmap(&path_dwp) {
 | 
			
		||||
            let map_dwp = stash.cache_mmap(map_dwp);
 | 
			
		||||
            if let Some(dwp_) = Object::parse(map_dwp) {
 | 
			
		||||
                return Some(dwp_);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ParsedSym {
 | 
			
		||||
    address: u64,
 | 
			
		||||
    size: u64,
 | 
			
		||||
    name: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Object<'a> {
 | 
			
		||||
    /// Zero-sized type representing the native endianness.
 | 
			
		||||
    ///
 | 
			
		||||
    /// We could use a literal instead, but this helps ensure correctness.
 | 
			
		||||
    endian: NativeEndian,
 | 
			
		||||
    /// The entire file data.
 | 
			
		||||
    data: &'a [u8],
 | 
			
		||||
    sections: SectionTable<'a, Elf>,
 | 
			
		||||
    strings: StringTable<'a>,
 | 
			
		||||
    /// List of pre-parsed and sorted symbols by base address.
 | 
			
		||||
    syms: Vec<ParsedSym>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Object<'a> {
 | 
			
		||||
    fn parse(data: &'a [u8]) -> Option<Object<'a>> {
 | 
			
		||||
        let elf = Elf::parse(data).ok()?;
 | 
			
		||||
        let endian = elf.endian().ok()?;
 | 
			
		||||
        let sections = elf.sections(endian, data).ok()?;
 | 
			
		||||
        let mut syms = sections
 | 
			
		||||
            .symbols(endian, data, object::elf::SHT_SYMTAB)
 | 
			
		||||
            .ok()?;
 | 
			
		||||
        if syms.is_empty() {
 | 
			
		||||
            syms = sections
 | 
			
		||||
                .symbols(endian, data, object::elf::SHT_DYNSYM)
 | 
			
		||||
                .ok()?;
 | 
			
		||||
        }
 | 
			
		||||
        let strings = syms.strings();
 | 
			
		||||
 | 
			
		||||
        let mut syms = syms
 | 
			
		||||
            .iter()
 | 
			
		||||
            // Only look at function/object symbols. This mirrors what
 | 
			
		||||
            // libbacktrace does and in general we're only symbolicating
 | 
			
		||||
            // function addresses in theory. Object symbols correspond
 | 
			
		||||
            // to data, and maybe someone's crazy enough to have a
 | 
			
		||||
            // function go into static data?
 | 
			
		||||
            .filter(|sym| {
 | 
			
		||||
                let st_type = sym.st_type();
 | 
			
		||||
                st_type == object::elf::STT_FUNC || st_type == object::elf::STT_OBJECT
 | 
			
		||||
            })
 | 
			
		||||
            // skip anything that's in an undefined section header,
 | 
			
		||||
            // since it means it's an imported function and we're only
 | 
			
		||||
            // symbolicating with locally defined functions.
 | 
			
		||||
            .filter(|sym| sym.st_shndx(endian) != object::elf::SHN_UNDEF)
 | 
			
		||||
            .map(|sym| {
 | 
			
		||||
                let address = sym.st_value(endian).into();
 | 
			
		||||
                let size = sym.st_size(endian).into();
 | 
			
		||||
                let name = sym.st_name(endian);
 | 
			
		||||
                ParsedSym {
 | 
			
		||||
                    address,
 | 
			
		||||
                    size,
 | 
			
		||||
                    name,
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
        syms.sort_unstable_by_key(|s| s.address);
 | 
			
		||||
        Some(Object {
 | 
			
		||||
            endian,
 | 
			
		||||
            data,
 | 
			
		||||
            sections,
 | 
			
		||||
            strings,
 | 
			
		||||
            syms,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn section(&self, stash: &'a Stash, name: &str) -> Option<&'a [u8]> {
 | 
			
		||||
        if let Some(section) = self.section_header(name) {
 | 
			
		||||
            let mut data = Bytes(section.data(self.endian, self.data).ok()?);
 | 
			
		||||
 | 
			
		||||
            // Check for DWARF-standard (gABI) compression, i.e., as generated
 | 
			
		||||
            // by ld's `--compress-debug-sections=zlib-gabi` flag.
 | 
			
		||||
            let flags: u64 = section.sh_flags(self.endian).into();
 | 
			
		||||
            if (flags & u64::from(SHF_COMPRESSED)) == 0 {
 | 
			
		||||
                // Not compressed.
 | 
			
		||||
                return Some(data.0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let header = data.read::<<Elf as FileHeader>::CompressionHeader>().ok()?;
 | 
			
		||||
            if header.ch_type(self.endian) != ELFCOMPRESS_ZLIB {
 | 
			
		||||
                // Zlib compression is the only known type.
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
            let size = usize::try_from(header.ch_size(self.endian)).ok()?;
 | 
			
		||||
            let buf = stash.allocate(size);
 | 
			
		||||
            decompress_zlib(data.0, buf)?;
 | 
			
		||||
            return Some(buf);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check for the nonstandard GNU compression format, i.e., as generated
 | 
			
		||||
        // by ld's `--compress-debug-sections=zlib-gnu` flag. This means that if
 | 
			
		||||
        // we're actually asking for `.debug_info` then we need to look up a
 | 
			
		||||
        // section named `.zdebug_info`.
 | 
			
		||||
        if !name.starts_with(".debug_") {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let debug_name = name[7..].as_bytes();
 | 
			
		||||
        let compressed_section = self
 | 
			
		||||
            .sections
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|header| {
 | 
			
		||||
                let name = self.sections.section_name(self.endian, header).ok()?;
 | 
			
		||||
                if name.starts_with(b".zdebug_") && &name[8..] == debug_name {
 | 
			
		||||
                    Some(header)
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .next()?;
 | 
			
		||||
        let mut data = Bytes(compressed_section.data(self.endian, self.data).ok()?);
 | 
			
		||||
        if data.read_bytes(8).ok()?.0 != b"ZLIB\0\0\0\0" {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let size = usize::try_from(data.read::<object::U32Bytes<_>>().ok()?.get(BigEndian)).ok()?;
 | 
			
		||||
        let buf = stash.allocate(size);
 | 
			
		||||
        decompress_zlib(data.0, buf)?;
 | 
			
		||||
        Some(buf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn section_header(&self, name: &str) -> Option<&<Elf as FileHeader>::SectionHeader> {
 | 
			
		||||
        self.sections
 | 
			
		||||
            .section_by_name(self.endian, name.as_bytes())
 | 
			
		||||
            .map(|(_index, section)| section)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
 | 
			
		||||
        // Same sort of binary search as Windows above
 | 
			
		||||
        let i = match self.syms.binary_search_by_key(&addr, |sym| sym.address) {
 | 
			
		||||
            Ok(i) => i,
 | 
			
		||||
            Err(i) => i.checked_sub(1)?,
 | 
			
		||||
        };
 | 
			
		||||
        let sym = self.syms.get(i)?;
 | 
			
		||||
        if sym.address <= addr && addr <= sym.address + sym.size {
 | 
			
		||||
            self.strings.get(sym.name).ok()
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn search_object_map(&self, _addr: u64) -> Option<(&Context<'_>, u64)> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn build_id(&self) -> Option<&'a [u8]> {
 | 
			
		||||
        for section in self.sections.iter() {
 | 
			
		||||
            if let Ok(Some(mut notes)) = section.notes(self.endian, self.data) {
 | 
			
		||||
                while let Ok(Some(note)) = notes.next() {
 | 
			
		||||
                    if note.name() == ELF_NOTE_GNU && note.n_type(self.endian) == NT_GNU_BUILD_ID {
 | 
			
		||||
                        return Some(note.desc());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // The contents of the ".gnu_debuglink" section is documented at:
 | 
			
		||||
    // https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
 | 
			
		||||
    fn gnu_debuglink_path(&self, path: &Path) -> Option<(PathBuf, u32)> {
 | 
			
		||||
        let section = self.section_header(".gnu_debuglink")?;
 | 
			
		||||
        let data = section.data(self.endian, self.data).ok()?;
 | 
			
		||||
        let len = data.iter().position(|x| *x == 0)?;
 | 
			
		||||
        let filename = &data[..len];
 | 
			
		||||
        let offset = (len + 1 + 3) & !3;
 | 
			
		||||
        let crc_bytes = data
 | 
			
		||||
            .get(offset..offset + 4)
 | 
			
		||||
            .and_then(|bytes| bytes.try_into().ok())?;
 | 
			
		||||
        let crc = u32::from_ne_bytes(crc_bytes);
 | 
			
		||||
        let path_debug = locate_debuglink(path, filename)?;
 | 
			
		||||
        Some((path_debug, crc))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // The format of the ".gnu_debugaltlink" section is based on gdb.
 | 
			
		||||
    fn gnu_debugaltlink_path(&self, path: &Path) -> Option<(PathBuf, &'a [u8])> {
 | 
			
		||||
        let section = self.section_header(".gnu_debugaltlink")?;
 | 
			
		||||
        let data = section.data(self.endian, self.data).ok()?;
 | 
			
		||||
        let len = data.iter().position(|x| *x == 0)?;
 | 
			
		||||
        let filename = &data[..len];
 | 
			
		||||
        let build_id = &data[len + 1..];
 | 
			
		||||
        let path_sup = locate_debugaltlink(path, filename, build_id)?;
 | 
			
		||||
        Some((path_sup, build_id))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn decompress_zlib(input: &[u8], output: &mut [u8]) -> Option<()> {
 | 
			
		||||
    use miniz_oxide::inflate::core::inflate_flags::{
 | 
			
		||||
        TINFL_FLAG_PARSE_ZLIB_HEADER, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF,
 | 
			
		||||
    };
 | 
			
		||||
    use miniz_oxide::inflate::core::{decompress, DecompressorOxide};
 | 
			
		||||
    use miniz_oxide::inflate::TINFLStatus;
 | 
			
		||||
 | 
			
		||||
    let (status, in_read, out_read) = decompress(
 | 
			
		||||
        &mut DecompressorOxide::new(),
 | 
			
		||||
        input,
 | 
			
		||||
        output,
 | 
			
		||||
        0,
 | 
			
		||||
        TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | TINFL_FLAG_PARSE_ZLIB_HEADER,
 | 
			
		||||
    );
 | 
			
		||||
    if status == TINFLStatus::Done && in_read == input.len() && out_read == output.len() {
 | 
			
		||||
        Some(())
 | 
			
		||||
    } else {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DEBUG_PATH: &[u8] = b"/usr/lib/debug";
 | 
			
		||||
 | 
			
		||||
fn debug_path_exists() -> bool {
 | 
			
		||||
    cfg_if::cfg_if! {
 | 
			
		||||
        if #[cfg(any(target_os = "freebsd", target_os = "linux"))] {
 | 
			
		||||
            use core::sync::atomic::{AtomicU8, Ordering};
 | 
			
		||||
            static DEBUG_PATH_EXISTS: AtomicU8 = AtomicU8::new(0);
 | 
			
		||||
 | 
			
		||||
            let mut exists = DEBUG_PATH_EXISTS.load(Ordering::Relaxed);
 | 
			
		||||
            if exists == 0 {
 | 
			
		||||
                exists = if Path::new(OsStr::from_bytes(DEBUG_PATH)).is_dir() {
 | 
			
		||||
                    1
 | 
			
		||||
                } else {
 | 
			
		||||
                    2
 | 
			
		||||
                };
 | 
			
		||||
                DEBUG_PATH_EXISTS.store(exists, Ordering::Relaxed);
 | 
			
		||||
            }
 | 
			
		||||
            exists == 1
 | 
			
		||||
        } else {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Locate a debug file based on its build ID.
 | 
			
		||||
///
 | 
			
		||||
/// The format of build id paths is documented at:
 | 
			
		||||
/// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
 | 
			
		||||
fn locate_build_id(build_id: &[u8]) -> Option<PathBuf> {
 | 
			
		||||
    const BUILD_ID_PATH: &[u8] = b"/usr/lib/debug/.build-id/";
 | 
			
		||||
    const BUILD_ID_SUFFIX: &[u8] = b".debug";
 | 
			
		||||
 | 
			
		||||
    if build_id.len() < 2 {
 | 
			
		||||
        return None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if !debug_path_exists() {
 | 
			
		||||
        return None;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut path =
 | 
			
		||||
        Vec::with_capacity(BUILD_ID_PATH.len() + BUILD_ID_SUFFIX.len() + build_id.len() * 2 + 1);
 | 
			
		||||
    path.extend(BUILD_ID_PATH);
 | 
			
		||||
    path.push(hex(build_id[0] >> 4));
 | 
			
		||||
    path.push(hex(build_id[0] & 0xf));
 | 
			
		||||
    path.push(b'/');
 | 
			
		||||
    for byte in &build_id[1..] {
 | 
			
		||||
        path.push(hex(byte >> 4));
 | 
			
		||||
        path.push(hex(byte & 0xf));
 | 
			
		||||
    }
 | 
			
		||||
    path.extend(BUILD_ID_SUFFIX);
 | 
			
		||||
    Some(PathBuf::from(OsString::from_vec(path)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn hex(byte: u8) -> u8 {
 | 
			
		||||
    if byte < 10 {
 | 
			
		||||
        b'0' + byte
 | 
			
		||||
    } else {
 | 
			
		||||
        b'a' + byte - 10
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Locate a file specified in a `.gnu_debuglink` section.
 | 
			
		||||
///
 | 
			
		||||
/// `path` is the file containing the section.
 | 
			
		||||
/// `filename` is from the contents of the section.
 | 
			
		||||
///
 | 
			
		||||
/// Search order is based on gdb, documented at:
 | 
			
		||||
/// https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
 | 
			
		||||
///
 | 
			
		||||
/// gdb also allows the user to customize the debug search path, but we don't.
 | 
			
		||||
///
 | 
			
		||||
/// gdb also supports debuginfod, but we don't yet.
 | 
			
		||||
fn locate_debuglink(path: &Path, filename: &[u8]) -> Option<PathBuf> {
 | 
			
		||||
    let path = fs::canonicalize(path).ok()?;
 | 
			
		||||
    let parent = path.parent()?;
 | 
			
		||||
    let mut f = PathBuf::from(OsString::with_capacity(
 | 
			
		||||
        DEBUG_PATH.len() + parent.as_os_str().len() + filename.len() + 2,
 | 
			
		||||
    ));
 | 
			
		||||
    let filename = Path::new(OsStr::from_bytes(filename));
 | 
			
		||||
 | 
			
		||||
    // Try "/parent/filename" if it differs from "path"
 | 
			
		||||
    f.push(parent);
 | 
			
		||||
    f.push(filename);
 | 
			
		||||
    if f != path && f.is_file() {
 | 
			
		||||
        return Some(f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Try "/parent/.debug/filename"
 | 
			
		||||
    let mut s = OsString::from(f);
 | 
			
		||||
    s.clear();
 | 
			
		||||
    f = PathBuf::from(s);
 | 
			
		||||
    f.push(parent);
 | 
			
		||||
    f.push(".debug");
 | 
			
		||||
    f.push(filename);
 | 
			
		||||
    if f.is_file() {
 | 
			
		||||
        return Some(f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if debug_path_exists() {
 | 
			
		||||
        // Try "/usr/lib/debug/parent/filename"
 | 
			
		||||
        let mut s = OsString::from(f);
 | 
			
		||||
        s.clear();
 | 
			
		||||
        f = PathBuf::from(s);
 | 
			
		||||
        f.push(OsStr::from_bytes(DEBUG_PATH));
 | 
			
		||||
        f.push(parent.strip_prefix("/").unwrap());
 | 
			
		||||
        f.push(filename);
 | 
			
		||||
        if f.is_file() {
 | 
			
		||||
            return Some(f);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Locate a file specified in a `.gnu_debugaltlink` section.
 | 
			
		||||
///
 | 
			
		||||
/// `path` is the file containing the section.
 | 
			
		||||
/// `filename` and `build_id` are the contents of the section.
 | 
			
		||||
///
 | 
			
		||||
/// Search order is based on gdb:
 | 
			
		||||
/// - filename, which is either absolute or relative to `path`
 | 
			
		||||
/// - the build ID path under `BUILD_ID_PATH`
 | 
			
		||||
///
 | 
			
		||||
/// gdb also allows the user to customize the debug search path, but we don't.
 | 
			
		||||
///
 | 
			
		||||
/// gdb also supports debuginfod, but we don't yet.
 | 
			
		||||
fn locate_debugaltlink(path: &Path, filename: &[u8], build_id: &[u8]) -> Option<PathBuf> {
 | 
			
		||||
    let filename = Path::new(OsStr::from_bytes(filename));
 | 
			
		||||
    if filename.is_absolute() {
 | 
			
		||||
        if filename.is_file() {
 | 
			
		||||
            return Some(filename.into());
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        let path = fs::canonicalize(path).ok()?;
 | 
			
		||||
        let parent = path.parent()?;
 | 
			
		||||
        let mut f = PathBuf::from(parent);
 | 
			
		||||
        f.push(filename);
 | 
			
		||||
        if f.is_file() {
 | 
			
		||||
            return Some(f);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    locate_build_id(build_id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn convert_path<R: gimli::Reader>(r: &R) -> Result<PathBuf, gimli::Error> {
 | 
			
		||||
    let bytes = r.to_slice()?;
 | 
			
		||||
    Ok(PathBuf::from(OsStr::from_bytes(&bytes)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(super) fn handle_split_dwarf<'data>(
 | 
			
		||||
    package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
 | 
			
		||||
    stash: &'data Stash,
 | 
			
		||||
    load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
 | 
			
		||||
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
 | 
			
		||||
    if let Some(dwp) = package.as_ref() {
 | 
			
		||||
        if let Ok(Some(cu)) = dwp.find_cu(load.dwo_id, &load.parent) {
 | 
			
		||||
            return Some(Arc::new(cu));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut path = PathBuf::new();
 | 
			
		||||
    if let Some(p) = load.comp_dir.as_ref() {
 | 
			
		||||
        path.push(convert_path(p).ok()?);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    path.push(convert_path(load.path.as_ref()?).ok()?);
 | 
			
		||||
 | 
			
		||||
    if let Some(map_dwo) = super::mmap(&path) {
 | 
			
		||||
        let map_dwo = stash.cache_mmap(map_dwo);
 | 
			
		||||
        if let Some(dwo) = Object::parse(map_dwo) {
 | 
			
		||||
            return gimli::Dwarf::load(|id| -> Result<_, ()> {
 | 
			
		||||
                let data = id
 | 
			
		||||
                    .dwo_name()
 | 
			
		||||
                    .and_then(|name| dwo.section(stash, name))
 | 
			
		||||
                    .unwrap_or(&[]);
 | 
			
		||||
                Ok(EndianSlice::new(data, Endian))
 | 
			
		||||
            })
 | 
			
		||||
            .ok()
 | 
			
		||||
            .map(|mut dwo_dwarf| {
 | 
			
		||||
                dwo_dwarf.make_dwo(&load.parent);
 | 
			
		||||
                Arc::new(dwo_dwarf)
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/backtrace/src/symbolize/gimli/libs_dl_iterate_phdr.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
// Other Unix (e.g. Linux) platforms use ELF as an object file format
 | 
			
		||||
// and typically implement an API called `dl_iterate_phdr` to load
 | 
			
		||||
// native libraries.
 | 
			
		||||
 | 
			
		||||
use super::mystd::borrow::ToOwned;
 | 
			
		||||
use super::mystd::env;
 | 
			
		||||
use super::mystd::ffi::{CStr, OsStr};
 | 
			
		||||
use super::mystd::os::unix::prelude::*;
 | 
			
		||||
use super::{Library, LibrarySegment, OsString, Vec};
 | 
			
		||||
use core::slice;
 | 
			
		||||
 | 
			
		||||
pub(super) fn native_libraries() -> Vec<Library> {
 | 
			
		||||
    let mut ret = Vec::new();
 | 
			
		||||
    unsafe {
 | 
			
		||||
        libc::dl_iterate_phdr(Some(callback), &mut ret as *mut Vec<_> as *mut _);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn infer_current_exe(base_addr: usize) -> OsString {
 | 
			
		||||
    if let Ok(entries) = super::parse_running_mmaps::parse_maps() {
 | 
			
		||||
        let opt_path = entries
 | 
			
		||||
            .iter()
 | 
			
		||||
            .find(|e| e.ip_matches(base_addr) && e.pathname().len() > 0)
 | 
			
		||||
            .map(|e| e.pathname())
 | 
			
		||||
            .cloned();
 | 
			
		||||
        if let Some(path) = opt_path {
 | 
			
		||||
            return path;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    env::current_exe().map(|e| e.into()).unwrap_or_default()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// `info` should be a valid pointers.
 | 
			
		||||
// `vec` should be a valid pointer to a `std::Vec`.
 | 
			
		||||
unsafe extern "C" fn callback(
 | 
			
		||||
    info: *mut libc::dl_phdr_info,
 | 
			
		||||
    _size: libc::size_t,
 | 
			
		||||
    vec: *mut libc::c_void,
 | 
			
		||||
) -> libc::c_int {
 | 
			
		||||
    let info = &*info;
 | 
			
		||||
    let libs = &mut *(vec as *mut Vec<Library>);
 | 
			
		||||
    let is_main_prog = info.dlpi_name.is_null() || *info.dlpi_name == 0;
 | 
			
		||||
    let name = if is_main_prog {
 | 
			
		||||
        // The man page for dl_iterate_phdr says that the first object visited by
 | 
			
		||||
        // callback is the main program; so the first time we encounter a
 | 
			
		||||
        // nameless entry, we can assume its the main program and try to infer its path.
 | 
			
		||||
        // After that, we cannot continue that assumption, and we use an empty string.
 | 
			
		||||
        if libs.is_empty() {
 | 
			
		||||
            infer_current_exe(info.dlpi_addr as usize)
 | 
			
		||||
        } else {
 | 
			
		||||
            OsString::new()
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        let bytes = CStr::from_ptr(info.dlpi_name).to_bytes();
 | 
			
		||||
        OsStr::from_bytes(bytes).to_owned()
 | 
			
		||||
    };
 | 
			
		||||
    let headers = slice::from_raw_parts(info.dlpi_phdr, info.dlpi_phnum as usize);
 | 
			
		||||
    libs.push(Library {
 | 
			
		||||
        name,
 | 
			
		||||
        segments: headers
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|header| LibrarySegment {
 | 
			
		||||
                len: (*header).p_memsz as usize,
 | 
			
		||||
                stated_virtual_memory_address: (*header).p_vaddr as usize,
 | 
			
		||||
            })
 | 
			
		||||
            .collect(),
 | 
			
		||||
        bias: info.dlpi_addr as usize,
 | 
			
		||||
    });
 | 
			
		||||
    0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										48
									
								
								vendor/backtrace/src/symbolize/gimli/libs_haiku.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/backtrace/src/symbolize/gimli/libs_haiku.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
// Haiku implements the image_info struct and the get_next_image_info()
 | 
			
		||||
// functions to iterate through the loaded executable images. The
 | 
			
		||||
// image_info struct contains a pointer to the start of the .text
 | 
			
		||||
// section within the virtual address space, as well as the size of
 | 
			
		||||
// that section. All the read-only segments of the ELF-binary are in
 | 
			
		||||
// that part of the address space.
 | 
			
		||||
 | 
			
		||||
use super::mystd::borrow::ToOwned;
 | 
			
		||||
use super::mystd::ffi::{CStr, OsStr};
 | 
			
		||||
use super::mystd::mem::MaybeUninit;
 | 
			
		||||
use super::mystd::os::unix::prelude::*;
 | 
			
		||||
use super::{Library, LibrarySegment, Vec};
 | 
			
		||||
 | 
			
		||||
pub(super) fn native_libraries() -> Vec<Library> {
 | 
			
		||||
    let mut libraries: Vec<Library> = Vec::new();
 | 
			
		||||
 | 
			
		||||
    unsafe {
 | 
			
		||||
        let mut info = MaybeUninit::<libc::image_info>::zeroed();
 | 
			
		||||
        let mut cookie: i32 = 0;
 | 
			
		||||
        // Load the first image to get a valid info struct
 | 
			
		||||
        let mut status =
 | 
			
		||||
            libc::get_next_image_info(libc::B_CURRENT_TEAM, &mut cookie, info.as_mut_ptr());
 | 
			
		||||
        if status != libc::B_OK {
 | 
			
		||||
            return libraries;
 | 
			
		||||
        }
 | 
			
		||||
        let mut info = info.assume_init();
 | 
			
		||||
 | 
			
		||||
        while status == libc::B_OK {
 | 
			
		||||
            let mut segments = Vec::new();
 | 
			
		||||
            segments.push(LibrarySegment {
 | 
			
		||||
                stated_virtual_memory_address: 0,
 | 
			
		||||
                len: info.text_size as usize,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            let bytes = CStr::from_ptr(info.name.as_ptr()).to_bytes();
 | 
			
		||||
            let name = OsStr::from_bytes(bytes).to_owned();
 | 
			
		||||
            libraries.push(Library {
 | 
			
		||||
                name: name,
 | 
			
		||||
                segments: segments,
 | 
			
		||||
                bias: info.text as usize,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            status = libc::get_next_image_info(libc::B_CURRENT_TEAM, &mut cookie, &mut info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    libraries
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										99
									
								
								vendor/backtrace/src/symbolize/gimli/libs_illumos.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								vendor/backtrace/src/symbolize/gimli/libs_illumos.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
use super::mystd::borrow::ToOwned;
 | 
			
		||||
use super::mystd::ffi::{CStr, OsStr};
 | 
			
		||||
use super::mystd::os::unix::prelude::*;
 | 
			
		||||
use super::{Library, LibrarySegment, Vec};
 | 
			
		||||
use core::mem;
 | 
			
		||||
use object::NativeEndian;
 | 
			
		||||
 | 
			
		||||
#[cfg(target_pointer_width = "64")]
 | 
			
		||||
use object::elf::{FileHeader64 as FileHeader, ProgramHeader64 as ProgramHeader};
 | 
			
		||||
 | 
			
		||||
type EHdr = FileHeader<NativeEndian>;
 | 
			
		||||
type PHdr = ProgramHeader<NativeEndian>;
 | 
			
		||||
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct LinkMap {
 | 
			
		||||
    l_addr: libc::c_ulong,
 | 
			
		||||
    l_name: *const libc::c_char,
 | 
			
		||||
    l_ld: *const libc::c_void,
 | 
			
		||||
    l_next: *const LinkMap,
 | 
			
		||||
    l_prev: *const LinkMap,
 | 
			
		||||
    l_refname: *const libc::c_char,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RTLD_SELF: *const libc::c_void = -3isize as *const libc::c_void;
 | 
			
		||||
const RTLD_DI_LINKMAP: libc::c_int = 2;
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
    fn dlinfo(
 | 
			
		||||
        handle: *const libc::c_void,
 | 
			
		||||
        request: libc::c_int,
 | 
			
		||||
        p: *mut libc::c_void,
 | 
			
		||||
    ) -> libc::c_int;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(super) fn native_libraries() -> Vec<Library> {
 | 
			
		||||
    let mut libs = Vec::new();
 | 
			
		||||
 | 
			
		||||
    // Request the current link map from the runtime linker:
 | 
			
		||||
    let map = unsafe {
 | 
			
		||||
        let mut map: *const LinkMap = mem::zeroed();
 | 
			
		||||
        if dlinfo(
 | 
			
		||||
            RTLD_SELF,
 | 
			
		||||
            RTLD_DI_LINKMAP,
 | 
			
		||||
            (&mut map) as *mut *const LinkMap as *mut libc::c_void,
 | 
			
		||||
        ) != 0
 | 
			
		||||
        {
 | 
			
		||||
            return libs;
 | 
			
		||||
        }
 | 
			
		||||
        map
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Each entry in the link map represents a loaded object:
 | 
			
		||||
    let mut l = map;
 | 
			
		||||
    while !l.is_null() {
 | 
			
		||||
        // Fetch the fully qualified path of the loaded object:
 | 
			
		||||
        let bytes = unsafe { CStr::from_ptr((*l).l_name) }.to_bytes();
 | 
			
		||||
        let name = OsStr::from_bytes(bytes).to_owned();
 | 
			
		||||
 | 
			
		||||
        // The base address of the object loaded into memory:
 | 
			
		||||
        let addr = unsafe { (*l).l_addr };
 | 
			
		||||
 | 
			
		||||
        // Use the ELF header for this object to locate the program
 | 
			
		||||
        // header:
 | 
			
		||||
        let e: *const EHdr = unsafe { (*l).l_addr as *const EHdr };
 | 
			
		||||
        let phoff = unsafe { (*e).e_phoff }.get(NativeEndian);
 | 
			
		||||
        let phnum = unsafe { (*e).e_phnum }.get(NativeEndian);
 | 
			
		||||
        let etype = unsafe { (*e).e_type }.get(NativeEndian);
 | 
			
		||||
 | 
			
		||||
        let phdr: *const PHdr = (addr + phoff) as *const PHdr;
 | 
			
		||||
        let phdr = unsafe { core::slice::from_raw_parts(phdr, phnum as usize) };
 | 
			
		||||
 | 
			
		||||
        libs.push(Library {
 | 
			
		||||
            name,
 | 
			
		||||
            segments: phdr
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|p| {
 | 
			
		||||
                    let memsz = p.p_memsz.get(NativeEndian);
 | 
			
		||||
                    let vaddr = p.p_vaddr.get(NativeEndian);
 | 
			
		||||
                    LibrarySegment {
 | 
			
		||||
                        len: memsz as usize,
 | 
			
		||||
                        stated_virtual_memory_address: vaddr as usize,
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .collect(),
 | 
			
		||||
            bias: if etype == object::elf::ET_EXEC {
 | 
			
		||||
                // Program header addresses for the base executable are
 | 
			
		||||
                // already absolute.
 | 
			
		||||
                0
 | 
			
		||||
            } else {
 | 
			
		||||
                // Other addresses are relative to the object base.
 | 
			
		||||
                addr as usize
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        l = unsafe { (*l).l_next };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    libs
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								vendor/backtrace/src/symbolize/gimli/libs_libnx.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								vendor/backtrace/src/symbolize/gimli/libs_libnx.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
use super::{Library, LibrarySegment, Vec};
 | 
			
		||||
 | 
			
		||||
// DevkitA64 doesn't natively support debug info, but the build system will
 | 
			
		||||
// place debug info at the path `romfs:/debug_info.elf`.
 | 
			
		||||
pub(super) fn native_libraries() -> Vec<Library> {
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        static __start__: u8;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let bias = unsafe { &__start__ } as *const u8 as usize;
 | 
			
		||||
 | 
			
		||||
    let mut ret = Vec::new();
 | 
			
		||||
    let mut segments = Vec::new();
 | 
			
		||||
    segments.push(LibrarySegment {
 | 
			
		||||
        stated_virtual_memory_address: 0,
 | 
			
		||||
        len: usize::max_value() - bias,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let path = "romfs:/debug_info.elf";
 | 
			
		||||
    ret.push(Library {
 | 
			
		||||
        name: path.into(),
 | 
			
		||||
        segments,
 | 
			
		||||
        bias,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    ret
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										146
									
								
								vendor/backtrace/src/symbolize/gimli/libs_macos.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								vendor/backtrace/src/symbolize/gimli/libs_macos.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
			
		||||
#![allow(deprecated)]
 | 
			
		||||
 | 
			
		||||
use super::mystd::ffi::{CStr, OsStr};
 | 
			
		||||
use super::mystd::os::unix::prelude::*;
 | 
			
		||||
use super::mystd::prelude::v1::*;
 | 
			
		||||
use super::{Library, LibrarySegment};
 | 
			
		||||
use core::convert::TryInto;
 | 
			
		||||
use core::mem;
 | 
			
		||||
 | 
			
		||||
pub(super) fn native_libraries() -> Vec<Library> {
 | 
			
		||||
    let mut ret = Vec::new();
 | 
			
		||||
    let images = unsafe { libc::_dyld_image_count() };
 | 
			
		||||
    for i in 0..images {
 | 
			
		||||
        ret.extend(native_library(i));
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn native_library(i: u32) -> Option<Library> {
 | 
			
		||||
    use object::macho;
 | 
			
		||||
    use object::read::macho::{MachHeader, Segment};
 | 
			
		||||
    use object::NativeEndian;
 | 
			
		||||
 | 
			
		||||
    // Fetch the name of this library which corresponds to the path of
 | 
			
		||||
    // where to load it as well.
 | 
			
		||||
    let name = unsafe {
 | 
			
		||||
        let name = libc::_dyld_get_image_name(i);
 | 
			
		||||
        if name.is_null() {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        CStr::from_ptr(name)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Load the image header of this library and delegate to `object` to
 | 
			
		||||
    // parse all the load commands so we can figure out all the segments
 | 
			
		||||
    // involved here.
 | 
			
		||||
    let (mut load_commands, endian) = unsafe {
 | 
			
		||||
        let header = libc::_dyld_get_image_header(i);
 | 
			
		||||
        if header.is_null() {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        match (*header).magic {
 | 
			
		||||
            macho::MH_MAGIC => {
 | 
			
		||||
                let endian = NativeEndian;
 | 
			
		||||
                let header = &*(header as *const macho::MachHeader32<NativeEndian>);
 | 
			
		||||
                let data = core::slice::from_raw_parts(
 | 
			
		||||
                    header as *const _ as *const u8,
 | 
			
		||||
                    mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize,
 | 
			
		||||
                );
 | 
			
		||||
                (header.load_commands(endian, data, 0).ok()?, endian)
 | 
			
		||||
            }
 | 
			
		||||
            macho::MH_MAGIC_64 => {
 | 
			
		||||
                let endian = NativeEndian;
 | 
			
		||||
                let header = &*(header as *const macho::MachHeader64<NativeEndian>);
 | 
			
		||||
                let data = core::slice::from_raw_parts(
 | 
			
		||||
                    header as *const _ as *const u8,
 | 
			
		||||
                    mem::size_of_val(header) + header.sizeofcmds.get(endian) as usize,
 | 
			
		||||
                );
 | 
			
		||||
                (header.load_commands(endian, data, 0).ok()?, endian)
 | 
			
		||||
            }
 | 
			
		||||
            _ => return None,
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Iterate over the segments and register known regions for segments
 | 
			
		||||
    // that we find. Additionally record information bout text segments
 | 
			
		||||
    // for processing later, see comments below.
 | 
			
		||||
    let mut segments = Vec::new();
 | 
			
		||||
    let mut first_text = 0;
 | 
			
		||||
    let mut text_fileoff_zero = false;
 | 
			
		||||
    while let Some(cmd) = load_commands.next().ok()? {
 | 
			
		||||
        if let Some((seg, _)) = cmd.segment_32().ok()? {
 | 
			
		||||
            if seg.name() == b"__TEXT" {
 | 
			
		||||
                first_text = segments.len();
 | 
			
		||||
                if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 {
 | 
			
		||||
                    text_fileoff_zero = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            segments.push(LibrarySegment {
 | 
			
		||||
                len: seg.vmsize(endian).try_into().ok()?,
 | 
			
		||||
                stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if let Some((seg, _)) = cmd.segment_64().ok()? {
 | 
			
		||||
            if seg.name() == b"__TEXT" {
 | 
			
		||||
                first_text = segments.len();
 | 
			
		||||
                if seg.fileoff(endian) == 0 && seg.filesize(endian) > 0 {
 | 
			
		||||
                    text_fileoff_zero = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            segments.push(LibrarySegment {
 | 
			
		||||
                len: seg.vmsize(endian).try_into().ok()?,
 | 
			
		||||
                stated_virtual_memory_address: seg.vmaddr(endian).try_into().ok()?,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Determine the "slide" for this library which ends up being the
 | 
			
		||||
    // bias we use to figure out where in memory objects are loaded.
 | 
			
		||||
    // This is a bit of a weird computation though and is the result of
 | 
			
		||||
    // trying a few things in the wild and seeing what sticks.
 | 
			
		||||
    //
 | 
			
		||||
    // The general idea is that the `bias` plus a segment's
 | 
			
		||||
    // `stated_virtual_memory_address` is going to be where in the
 | 
			
		||||
    // actual address space the segment resides. The other thing we rely
 | 
			
		||||
    // on though is that a real address minus the `bias` is the index to
 | 
			
		||||
    // look up in the symbol table and debuginfo.
 | 
			
		||||
    //
 | 
			
		||||
    // It turns out, though, that for system loaded libraries these
 | 
			
		||||
    // calculations are incorrect. For native executables, however, it
 | 
			
		||||
    // appears correct. Lifting some logic from LLDB's source it has
 | 
			
		||||
    // some special-casing for the first `__TEXT` section loaded from
 | 
			
		||||
    // file offset 0 with a nonzero size. For whatever reason when this
 | 
			
		||||
    // is present it appears to mean that the symbol table is relative
 | 
			
		||||
    // to just the vmaddr slide for the library. If it's *not* present
 | 
			
		||||
    // then the symbol table is relative to the vmaddr slide plus the
 | 
			
		||||
    // segment's stated address.
 | 
			
		||||
    //
 | 
			
		||||
    // To handle this situation if we *don't* find a text section at
 | 
			
		||||
    // file offset zero then we increase the bias by the first text
 | 
			
		||||
    // sections's stated address and decrease all stated addresses by
 | 
			
		||||
    // that amount as well. That way the symbol table is always appears
 | 
			
		||||
    // relative to the library's bias amount. This appears to have the
 | 
			
		||||
    // right results for symbolizing via the symbol table.
 | 
			
		||||
    //
 | 
			
		||||
    // Honestly I'm not entirely sure whether this is right or if
 | 
			
		||||
    // there's something else that should indicate how to do this. For
 | 
			
		||||
    // now though this seems to work well enough (?) and we should
 | 
			
		||||
    // always be able to tweak this over time if necessary.
 | 
			
		||||
    //
 | 
			
		||||
    // For some more information see #318
 | 
			
		||||
    let mut slide = unsafe { libc::_dyld_get_image_vmaddr_slide(i) as usize };
 | 
			
		||||
    if !text_fileoff_zero {
 | 
			
		||||
        let adjust = segments[first_text].stated_virtual_memory_address;
 | 
			
		||||
        for segment in segments.iter_mut() {
 | 
			
		||||
            segment.stated_virtual_memory_address -= adjust;
 | 
			
		||||
        }
 | 
			
		||||
        slide += adjust;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Some(Library {
 | 
			
		||||
        name: OsStr::from_bytes(name.to_bytes()).to_owned(),
 | 
			
		||||
        segments,
 | 
			
		||||
        bias: slide,
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								vendor/backtrace/src/symbolize/gimli/libs_windows.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								vendor/backtrace/src/symbolize/gimli/libs_windows.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
use super::super::super::windows::*;
 | 
			
		||||
use super::mystd::os::windows::prelude::*;
 | 
			
		||||
use super::{coff, mmap, Library, LibrarySegment, OsString};
 | 
			
		||||
use alloc::vec;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use core::mem;
 | 
			
		||||
use core::mem::MaybeUninit;
 | 
			
		||||
 | 
			
		||||
// For loading native libraries on Windows, see some discussion on
 | 
			
		||||
// rust-lang/rust#71060 for the various strategies here.
 | 
			
		||||
pub(super) fn native_libraries() -> Vec<Library> {
 | 
			
		||||
    let mut ret = Vec::new();
 | 
			
		||||
    unsafe {
 | 
			
		||||
        add_loaded_images(&mut ret);
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn add_loaded_images(ret: &mut Vec<Library>) {
 | 
			
		||||
    let snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
 | 
			
		||||
    if snap == INVALID_HANDLE_VALUE {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut me = MaybeUninit::<MODULEENTRY32W>::zeroed().assume_init();
 | 
			
		||||
    me.dwSize = mem::size_of_val(&me) as DWORD;
 | 
			
		||||
    if Module32FirstW(snap, &mut me) == TRUE {
 | 
			
		||||
        loop {
 | 
			
		||||
            if let Some(lib) = load_library(&me) {
 | 
			
		||||
                ret.push(lib);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if Module32NextW(snap, &mut me) != TRUE {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    CloseHandle(snap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe fn load_library(me: &MODULEENTRY32W) -> Option<Library> {
 | 
			
		||||
    let pos = me
 | 
			
		||||
        .szExePath
 | 
			
		||||
        .iter()
 | 
			
		||||
        .position(|i| *i == 0)
 | 
			
		||||
        .unwrap_or(me.szExePath.len());
 | 
			
		||||
    let name = OsString::from_wide(&me.szExePath[..pos]);
 | 
			
		||||
 | 
			
		||||
    // MinGW libraries currently don't support ASLR
 | 
			
		||||
    // (rust-lang/rust#16514), but DLLs can still be relocated around in
 | 
			
		||||
    // the address space. It appears that addresses in debug info are
 | 
			
		||||
    // all as-if this library was loaded at its "image base", which is a
 | 
			
		||||
    // field in its COFF file headers. Since this is what debuginfo
 | 
			
		||||
    // seems to list we parse the symbol table and store addresses as if
 | 
			
		||||
    // the library was loaded at "image base" as well.
 | 
			
		||||
    //
 | 
			
		||||
    // The library may not be loaded at "image base", however.
 | 
			
		||||
    // (presumably something else may be loaded there?) This is where
 | 
			
		||||
    // the `bias` field comes into play, and we need to figure out the
 | 
			
		||||
    // value of `bias` here. Unfortunately though it's not clear how to
 | 
			
		||||
    // acquire this from a loaded module. What we do have, however, is
 | 
			
		||||
    // the actual load address (`modBaseAddr`).
 | 
			
		||||
    //
 | 
			
		||||
    // As a bit of a cop-out for now we mmap the file, read the file
 | 
			
		||||
    // header information, then drop the mmap. This is wasteful because
 | 
			
		||||
    // we'll probably reopen the mmap later, but this should work well
 | 
			
		||||
    // enough for now.
 | 
			
		||||
    //
 | 
			
		||||
    // Once we have the `image_base` (desired load location) and the
 | 
			
		||||
    // `base_addr` (actual load location) we can fill in the `bias`
 | 
			
		||||
    // (difference between the actual and desired) and then the stated
 | 
			
		||||
    // address of each segment is the `image_base` since that's what the
 | 
			
		||||
    // file says.
 | 
			
		||||
    //
 | 
			
		||||
    // For now it appears that unlike ELF/MachO we can make do with one
 | 
			
		||||
    // segment per library, using `modBaseSize` as the whole size.
 | 
			
		||||
    let mmap = mmap(name.as_ref())?;
 | 
			
		||||
    let image_base = coff::get_image_base(&mmap)?;
 | 
			
		||||
    let base_addr = me.modBaseAddr as usize;
 | 
			
		||||
    Some(Library {
 | 
			
		||||
        name,
 | 
			
		||||
        bias: base_addr.wrapping_sub(image_base),
 | 
			
		||||
        segments: vec![LibrarySegment {
 | 
			
		||||
            stated_virtual_memory_address: image_base,
 | 
			
		||||
            len: me.modBaseSize as usize,
 | 
			
		||||
        }],
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										333
									
								
								vendor/backtrace/src/symbolize/gimli/macho.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								vendor/backtrace/src/symbolize/gimli/macho.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,333 @@
 | 
			
		||||
use super::{gimli, Box, Context, Endian, EndianSlice, Mapping, Path, Stash, Vec};
 | 
			
		||||
use alloc::sync::Arc;
 | 
			
		||||
use core::convert::TryInto;
 | 
			
		||||
use object::macho;
 | 
			
		||||
use object::read::macho::{MachHeader, Nlist, Section, Segment as _};
 | 
			
		||||
use object::{Bytes, NativeEndian};
 | 
			
		||||
 | 
			
		||||
#[cfg(target_pointer_width = "32")]
 | 
			
		||||
type Mach = object::macho::MachHeader32<NativeEndian>;
 | 
			
		||||
#[cfg(target_pointer_width = "64")]
 | 
			
		||||
type Mach = object::macho::MachHeader64<NativeEndian>;
 | 
			
		||||
type MachSegment = <Mach as MachHeader>::Segment;
 | 
			
		||||
type MachSection = <Mach as MachHeader>::Section;
 | 
			
		||||
type MachNlist = <Mach as MachHeader>::Nlist;
 | 
			
		||||
 | 
			
		||||
impl Mapping {
 | 
			
		||||
    // The loading path for macOS is so different we just have a completely
 | 
			
		||||
    // different implementation of the function here. On macOS we need to go
 | 
			
		||||
    // probing the filesystem for a bunch of files.
 | 
			
		||||
    pub fn new(path: &Path) -> Option<Mapping> {
 | 
			
		||||
        // First up we need to load the unique UUID which is stored in the macho
 | 
			
		||||
        // header of the file we're reading, specified at `path`.
 | 
			
		||||
        let map = super::mmap(path)?;
 | 
			
		||||
        let (macho, data) = find_header(&map)?;
 | 
			
		||||
        let endian = macho.endian().ok()?;
 | 
			
		||||
        let uuid = macho.uuid(endian, data, 0).ok()?;
 | 
			
		||||
 | 
			
		||||
        // Next we need to look for a `*.dSYM` file. For now we just probe the
 | 
			
		||||
        // containing directory and look around for something that matches
 | 
			
		||||
        // `*.dSYM`. Once it's found we root through the dwarf resources that it
 | 
			
		||||
        // contains and try to find a macho file which has a matching UUID as
 | 
			
		||||
        // the one of our own file. If we find a match that's the dwarf file we
 | 
			
		||||
        // want to return.
 | 
			
		||||
        if let Some(uuid) = uuid {
 | 
			
		||||
            if let Some(parent) = path.parent() {
 | 
			
		||||
                if let Some(mapping) = Mapping::load_dsym(parent, uuid) {
 | 
			
		||||
                    return Some(mapping);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Looks like nothing matched our UUID, so let's at least return our own
 | 
			
		||||
        // file. This should have the symbol table for at least some
 | 
			
		||||
        // symbolication purposes.
 | 
			
		||||
        Mapping::mk(map, |data, stash| {
 | 
			
		||||
            let (macho, data) = find_header(data)?;
 | 
			
		||||
            let endian = macho.endian().ok()?;
 | 
			
		||||
            let obj = Object::parse(macho, endian, data)?;
 | 
			
		||||
            Context::new(stash, obj, None, None)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn load_dsym(dir: &Path, uuid: [u8; 16]) -> Option<Mapping> {
 | 
			
		||||
        for entry in dir.read_dir().ok()? {
 | 
			
		||||
            let entry = entry.ok()?;
 | 
			
		||||
            let filename = match entry.file_name().into_string() {
 | 
			
		||||
                Ok(name) => name,
 | 
			
		||||
                Err(_) => continue,
 | 
			
		||||
            };
 | 
			
		||||
            if !filename.ends_with(".dSYM") {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            let candidates = entry.path().join("Contents/Resources/DWARF");
 | 
			
		||||
            if let Some(mapping) = Mapping::try_dsym_candidate(&candidates, uuid) {
 | 
			
		||||
                return Some(mapping);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn try_dsym_candidate(dir: &Path, uuid: [u8; 16]) -> Option<Mapping> {
 | 
			
		||||
        // Look for files in the `DWARF` directory which have a matching uuid to
 | 
			
		||||
        // the original object file. If we find one then we found the debug
 | 
			
		||||
        // information.
 | 
			
		||||
        for entry in dir.read_dir().ok()? {
 | 
			
		||||
            let entry = entry.ok()?;
 | 
			
		||||
            let map = super::mmap(&entry.path())?;
 | 
			
		||||
            let candidate = Mapping::mk(map, |data, stash| {
 | 
			
		||||
                let (macho, data) = find_header(data)?;
 | 
			
		||||
                let endian = macho.endian().ok()?;
 | 
			
		||||
                let entry_uuid = macho.uuid(endian, data, 0).ok()??;
 | 
			
		||||
                if entry_uuid != uuid {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
                let obj = Object::parse(macho, endian, data)?;
 | 
			
		||||
                Context::new(stash, obj, None, None)
 | 
			
		||||
            });
 | 
			
		||||
            if let Some(candidate) = candidate {
 | 
			
		||||
                return Some(candidate);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn find_header(data: &'_ [u8]) -> Option<(&'_ Mach, &'_ [u8])> {
 | 
			
		||||
    use object::endian::BigEndian;
 | 
			
		||||
 | 
			
		||||
    let desired_cpu = || {
 | 
			
		||||
        if cfg!(target_arch = "x86") {
 | 
			
		||||
            Some(macho::CPU_TYPE_X86)
 | 
			
		||||
        } else if cfg!(target_arch = "x86_64") {
 | 
			
		||||
            Some(macho::CPU_TYPE_X86_64)
 | 
			
		||||
        } else if cfg!(target_arch = "arm") {
 | 
			
		||||
            Some(macho::CPU_TYPE_ARM)
 | 
			
		||||
        } else if cfg!(target_arch = "aarch64") {
 | 
			
		||||
            Some(macho::CPU_TYPE_ARM64)
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let mut data = Bytes(data);
 | 
			
		||||
    match data
 | 
			
		||||
        .clone()
 | 
			
		||||
        .read::<object::endian::U32<NativeEndian>>()
 | 
			
		||||
        .ok()?
 | 
			
		||||
        .get(NativeEndian)
 | 
			
		||||
    {
 | 
			
		||||
        macho::MH_MAGIC_64 | macho::MH_CIGAM_64 | macho::MH_MAGIC | macho::MH_CIGAM => {}
 | 
			
		||||
 | 
			
		||||
        macho::FAT_MAGIC | macho::FAT_CIGAM => {
 | 
			
		||||
            let mut header_data = data;
 | 
			
		||||
            let endian = BigEndian;
 | 
			
		||||
            let header = header_data.read::<macho::FatHeader>().ok()?;
 | 
			
		||||
            let nfat = header.nfat_arch.get(endian);
 | 
			
		||||
            let arch = (0..nfat)
 | 
			
		||||
                .filter_map(|_| header_data.read::<macho::FatArch32>().ok())
 | 
			
		||||
                .find(|arch| desired_cpu() == Some(arch.cputype.get(endian)))?;
 | 
			
		||||
            let offset = arch.offset.get(endian);
 | 
			
		||||
            let size = arch.size.get(endian);
 | 
			
		||||
            data = data
 | 
			
		||||
                .read_bytes_at(offset.try_into().ok()?, size.try_into().ok()?)
 | 
			
		||||
                .ok()?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        macho::FAT_MAGIC_64 | macho::FAT_CIGAM_64 => {
 | 
			
		||||
            let mut header_data = data;
 | 
			
		||||
            let endian = BigEndian;
 | 
			
		||||
            let header = header_data.read::<macho::FatHeader>().ok()?;
 | 
			
		||||
            let nfat = header.nfat_arch.get(endian);
 | 
			
		||||
            let arch = (0..nfat)
 | 
			
		||||
                .filter_map(|_| header_data.read::<macho::FatArch64>().ok())
 | 
			
		||||
                .find(|arch| desired_cpu() == Some(arch.cputype.get(endian)))?;
 | 
			
		||||
            let offset = arch.offset.get(endian);
 | 
			
		||||
            let size = arch.size.get(endian);
 | 
			
		||||
            data = data
 | 
			
		||||
                .read_bytes_at(offset.try_into().ok()?, size.try_into().ok()?)
 | 
			
		||||
                .ok()?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _ => return None,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Mach::parse(data.0, 0).ok().map(|h| (h, data.0))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This is used both for executables/libraries and source object files.
 | 
			
		||||
pub struct Object<'a> {
 | 
			
		||||
    endian: NativeEndian,
 | 
			
		||||
    data: &'a [u8],
 | 
			
		||||
    dwarf: Option<&'a [MachSection]>,
 | 
			
		||||
    syms: Vec<(&'a [u8], u64)>,
 | 
			
		||||
    syms_sort_by_name: bool,
 | 
			
		||||
    // Only set for executables/libraries, and not the source object files.
 | 
			
		||||
    object_map: Option<object::ObjectMap<'a>>,
 | 
			
		||||
    // The outer Option is for lazy loading, and the inner Option allows load errors to be cached.
 | 
			
		||||
    object_mappings: Box<[Option<Option<Mapping>>]>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Object<'a> {
 | 
			
		||||
    fn parse(mach: &'a Mach, endian: NativeEndian, data: &'a [u8]) -> Option<Object<'a>> {
 | 
			
		||||
        let is_object = mach.filetype(endian) == object::macho::MH_OBJECT;
 | 
			
		||||
        let mut dwarf = None;
 | 
			
		||||
        let mut syms = Vec::new();
 | 
			
		||||
        let mut syms_sort_by_name = false;
 | 
			
		||||
        let mut commands = mach.load_commands(endian, data, 0).ok()?;
 | 
			
		||||
        let mut object_map = None;
 | 
			
		||||
        let mut object_mappings = Vec::new();
 | 
			
		||||
        while let Ok(Some(command)) = commands.next() {
 | 
			
		||||
            if let Some((segment, section_data)) = MachSegment::from_command(command).ok()? {
 | 
			
		||||
                // Object files should have all sections in a single unnamed segment load command.
 | 
			
		||||
                if segment.name() == b"__DWARF" || (is_object && segment.name() == b"") {
 | 
			
		||||
                    dwarf = segment.sections(endian, section_data).ok();
 | 
			
		||||
                }
 | 
			
		||||
            } else if let Some(symtab) = command.symtab().ok()? {
 | 
			
		||||
                let symbols = symtab.symbols::<Mach, _>(endian, data).ok()?;
 | 
			
		||||
                syms = symbols
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .filter_map(|nlist: &MachNlist| {
 | 
			
		||||
                        let name = nlist.name(endian, symbols.strings()).ok()?;
 | 
			
		||||
                        if name.len() > 0 && nlist.is_definition() {
 | 
			
		||||
                            Some((name, u64::from(nlist.n_value(endian))))
 | 
			
		||||
                        } else {
 | 
			
		||||
                            None
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                    .collect();
 | 
			
		||||
                if is_object {
 | 
			
		||||
                    // We never search object file symbols by address.
 | 
			
		||||
                    // Instead, we already know the symbol name from the executable, and we
 | 
			
		||||
                    // need to search by name to find the matching symbol in the object file.
 | 
			
		||||
                    syms.sort_unstable_by_key(|(name, _)| *name);
 | 
			
		||||
                    syms_sort_by_name = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    syms.sort_unstable_by_key(|(_, addr)| *addr);
 | 
			
		||||
                    let map = symbols.object_map(endian);
 | 
			
		||||
                    object_mappings.resize_with(map.objects().len(), || None);
 | 
			
		||||
                    object_map = Some(map);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Some(Object {
 | 
			
		||||
            endian,
 | 
			
		||||
            data,
 | 
			
		||||
            dwarf,
 | 
			
		||||
            syms,
 | 
			
		||||
            syms_sort_by_name,
 | 
			
		||||
            object_map,
 | 
			
		||||
            object_mappings: object_mappings.into_boxed_slice(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn section(&self, _: &Stash, name: &str) -> Option<&'a [u8]> {
 | 
			
		||||
        let name = name.as_bytes();
 | 
			
		||||
        let dwarf = self.dwarf?;
 | 
			
		||||
        let section = dwarf.into_iter().find(|section| {
 | 
			
		||||
            let section_name = section.name();
 | 
			
		||||
            section_name == name || {
 | 
			
		||||
                section_name.starts_with(b"__")
 | 
			
		||||
                    && name.starts_with(b".")
 | 
			
		||||
                    && §ion_name[2..] == &name[1..]
 | 
			
		||||
            }
 | 
			
		||||
        })?;
 | 
			
		||||
        Some(section.data(self.endian, self.data).ok()?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> {
 | 
			
		||||
        debug_assert!(!self.syms_sort_by_name);
 | 
			
		||||
        let i = match self.syms.binary_search_by_key(&addr, |(_, addr)| *addr) {
 | 
			
		||||
            Ok(i) => i,
 | 
			
		||||
            Err(i) => i.checked_sub(1)?,
 | 
			
		||||
        };
 | 
			
		||||
        let (sym, _addr) = self.syms.get(i)?;
 | 
			
		||||
        Some(sym)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Try to load a context for an object file.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If dsymutil was not run, then the DWARF may be found in the source object files.
 | 
			
		||||
    pub(super) fn search_object_map<'b>(&'b mut self, addr: u64) -> Option<(&Context<'b>, u64)> {
 | 
			
		||||
        // `object_map` contains a map from addresses to symbols and object paths.
 | 
			
		||||
        // Look up the address and get a mapping for the object.
 | 
			
		||||
        let object_map = self.object_map.as_ref()?;
 | 
			
		||||
        let symbol = object_map.get(addr)?;
 | 
			
		||||
        let object_index = symbol.object_index();
 | 
			
		||||
        let mapping = self.object_mappings.get_mut(object_index)?;
 | 
			
		||||
        if mapping.is_none() {
 | 
			
		||||
            // No cached mapping, so create it.
 | 
			
		||||
            *mapping = Some(object_mapping(object_map.objects().get(object_index)?));
 | 
			
		||||
        }
 | 
			
		||||
        let cx: &'b Context<'static> = &mapping.as_ref()?.as_ref()?.cx;
 | 
			
		||||
        // Don't leak the `'static` lifetime, make sure it's scoped to just ourselves.
 | 
			
		||||
        let cx = unsafe { core::mem::transmute::<&'b Context<'static>, &'b Context<'b>>(cx) };
 | 
			
		||||
 | 
			
		||||
        // We must translate the address in order to be able to look it up
 | 
			
		||||
        // in the DWARF in the object file.
 | 
			
		||||
        debug_assert!(cx.object.syms.is_empty() || cx.object.syms_sort_by_name);
 | 
			
		||||
        let i = cx
 | 
			
		||||
            .object
 | 
			
		||||
            .syms
 | 
			
		||||
            .binary_search_by_key(&symbol.name(), |(name, _)| *name)
 | 
			
		||||
            .ok()?;
 | 
			
		||||
        let object_symbol = cx.object.syms.get(i)?;
 | 
			
		||||
        let object_addr = addr
 | 
			
		||||
            .wrapping_sub(symbol.address())
 | 
			
		||||
            .wrapping_add(object_symbol.1);
 | 
			
		||||
        Some((cx, object_addr))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn object_mapping(path: &[u8]) -> Option<Mapping> {
 | 
			
		||||
    use super::mystd::ffi::OsStr;
 | 
			
		||||
    use super::mystd::os::unix::prelude::*;
 | 
			
		||||
 | 
			
		||||
    let map;
 | 
			
		||||
 | 
			
		||||
    // `N_OSO` symbol names can be either `/path/to/object.o` or `/path/to/archive.a(object.o)`.
 | 
			
		||||
    let member_name = if let Some((archive_path, member_name)) = split_archive_path(path) {
 | 
			
		||||
        map = super::mmap(Path::new(OsStr::from_bytes(archive_path)))?;
 | 
			
		||||
        Some(member_name)
 | 
			
		||||
    } else {
 | 
			
		||||
        map = super::mmap(Path::new(OsStr::from_bytes(path)))?;
 | 
			
		||||
        None
 | 
			
		||||
    };
 | 
			
		||||
    Mapping::mk(map, |data, stash| {
 | 
			
		||||
        let data = match member_name {
 | 
			
		||||
            Some(member_name) => {
 | 
			
		||||
                let archive = object::read::archive::ArchiveFile::parse(data).ok()?;
 | 
			
		||||
                let member = archive
 | 
			
		||||
                    .members()
 | 
			
		||||
                    .filter_map(Result::ok)
 | 
			
		||||
                    .find(|m| m.name() == member_name)?;
 | 
			
		||||
                member.data(data).ok()?
 | 
			
		||||
            }
 | 
			
		||||
            None => data,
 | 
			
		||||
        };
 | 
			
		||||
        let (macho, data) = find_header(data)?;
 | 
			
		||||
        let endian = macho.endian().ok()?;
 | 
			
		||||
        let obj = Object::parse(macho, endian, data)?;
 | 
			
		||||
        Context::new(stash, obj, None, None)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn split_archive_path(path: &[u8]) -> Option<(&[u8], &[u8])> {
 | 
			
		||||
    let (last, path) = path.split_last()?;
 | 
			
		||||
    if *last != b')' {
 | 
			
		||||
        return None;
 | 
			
		||||
    }
 | 
			
		||||
    let index = path.iter().position(|&x| x == b'(')?;
 | 
			
		||||
    let (archive, rest) = path.split_at(index);
 | 
			
		||||
    Some((archive, &rest[1..]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(super) fn handle_split_dwarf<'data>(
 | 
			
		||||
    _package: Option<&gimli::DwarfPackage<EndianSlice<'data, Endian>>>,
 | 
			
		||||
    _stash: &'data Stash,
 | 
			
		||||
    _load: addr2line::SplitDwarfLoad<EndianSlice<'data, Endian>>,
 | 
			
		||||
) -> Option<Arc<gimli::Dwarf<EndianSlice<'data, Endian>>>> {
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/backtrace/src/symbolize/gimli/mmap_fake.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/backtrace/src/symbolize/gimli/mmap_fake.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
use super::{mystd::io::Read, File};
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use core::ops::Deref;
 | 
			
		||||
 | 
			
		||||
pub struct Mmap {
 | 
			
		||||
    vec: Vec<u8>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mmap {
 | 
			
		||||
    pub unsafe fn map(mut file: &File, len: usize) -> Option<Mmap> {
 | 
			
		||||
        let mut mmap = Mmap {
 | 
			
		||||
            vec: Vec::with_capacity(len),
 | 
			
		||||
        };
 | 
			
		||||
        file.read_to_end(&mut mmap.vec).ok()?;
 | 
			
		||||
        Some(mmap)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for Mmap {
 | 
			
		||||
    type Target = [u8];
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &[u8] {
 | 
			
		||||
        &self.vec[..]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								vendor/backtrace/src/symbolize/gimli/mmap_unix.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/backtrace/src/symbolize/gimli/mmap_unix.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
use super::mystd::fs::File;
 | 
			
		||||
use super::mystd::os::unix::prelude::*;
 | 
			
		||||
use core::ops::Deref;
 | 
			
		||||
use core::ptr;
 | 
			
		||||
use core::slice;
 | 
			
		||||
 | 
			
		||||
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
 | 
			
		||||
use libc::mmap as mmap64;
 | 
			
		||||
#[cfg(all(target_os = "linux", target_env = "gnu"))]
 | 
			
		||||
use libc::mmap64;
 | 
			
		||||
 | 
			
		||||
pub struct Mmap {
 | 
			
		||||
    ptr: *mut libc::c_void,
 | 
			
		||||
    len: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mmap {
 | 
			
		||||
    pub unsafe fn map(file: &File, len: usize) -> Option<Mmap> {
 | 
			
		||||
        let ptr = mmap64(
 | 
			
		||||
            ptr::null_mut(),
 | 
			
		||||
            len,
 | 
			
		||||
            libc::PROT_READ,
 | 
			
		||||
            libc::MAP_PRIVATE,
 | 
			
		||||
            file.as_raw_fd(),
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        if ptr == libc::MAP_FAILED {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        Some(Mmap { ptr, len })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for Mmap {
 | 
			
		||||
    type Target = [u8];
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &[u8] {
 | 
			
		||||
        unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Mmap {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let r = libc::munmap(self.ptr, self.len);
 | 
			
		||||
            debug_assert_eq!(r, 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								vendor/backtrace/src/symbolize/gimli/mmap_windows.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								vendor/backtrace/src/symbolize/gimli/mmap_windows.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
use super::super::super::windows::*;
 | 
			
		||||
use super::mystd::fs::File;
 | 
			
		||||
use super::mystd::os::windows::prelude::*;
 | 
			
		||||
use core::ops::Deref;
 | 
			
		||||
use core::ptr;
 | 
			
		||||
use core::slice;
 | 
			
		||||
 | 
			
		||||
pub struct Mmap {
 | 
			
		||||
    // keep the file alive to prevent it from being deleted which would cause
 | 
			
		||||
    // us to read bad data.
 | 
			
		||||
    _file: File,
 | 
			
		||||
    ptr: *mut c_void,
 | 
			
		||||
    len: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Mmap {
 | 
			
		||||
    pub unsafe fn map(file: &File, len: usize) -> Option<Mmap> {
 | 
			
		||||
        let file = file.try_clone().ok()?;
 | 
			
		||||
        let mapping = CreateFileMappingA(
 | 
			
		||||
            file.as_raw_handle() as *mut _,
 | 
			
		||||
            ptr::null_mut(),
 | 
			
		||||
            PAGE_READONLY,
 | 
			
		||||
            0,
 | 
			
		||||
            0,
 | 
			
		||||
            ptr::null(),
 | 
			
		||||
        );
 | 
			
		||||
        if mapping.is_null() {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let ptr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, len);
 | 
			
		||||
        CloseHandle(mapping);
 | 
			
		||||
        if ptr.is_null() {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        Some(Mmap {
 | 
			
		||||
            _file: file,
 | 
			
		||||
            ptr,
 | 
			
		||||
            len,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Deref for Mmap {
 | 
			
		||||
    type Target = [u8];
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &[u8] {
 | 
			
		||||
        unsafe { slice::from_raw_parts(self.ptr as *const u8, self.len) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Mmap {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let r = UnmapViewOfFile(self.ptr);
 | 
			
		||||
            debug_assert!(r != 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										295
									
								
								vendor/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										295
									
								
								vendor/backtrace/src/symbolize/gimli/parse_running_mmaps_unix.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,295 @@
 | 
			
		||||
// Note: This file is only currently used on targets that call out to the code
 | 
			
		||||
// in `mod libs_dl_iterate_phdr` (e.g. linux, freebsd, ...); it may be more
 | 
			
		||||
// general purpose, but it hasn't been tested elsewhere.
 | 
			
		||||
 | 
			
		||||
use super::mystd::fs::File;
 | 
			
		||||
use super::mystd::io::Read;
 | 
			
		||||
use super::mystd::str::FromStr;
 | 
			
		||||
use super::{OsString, String, Vec};
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq, Debug)]
 | 
			
		||||
pub(super) struct MapsEntry {
 | 
			
		||||
    /// start (inclusive) and limit (exclusive) of address range.
 | 
			
		||||
    address: (usize, usize),
 | 
			
		||||
    /// The perms field are the permissions for the entry
 | 
			
		||||
    ///
 | 
			
		||||
    /// r = read
 | 
			
		||||
    /// w = write
 | 
			
		||||
    /// x = execute
 | 
			
		||||
    /// s = shared
 | 
			
		||||
    /// p = private (copy on write)
 | 
			
		||||
    perms: [char; 4],
 | 
			
		||||
    /// Offset into the file (or "whatever").
 | 
			
		||||
    offset: usize,
 | 
			
		||||
    /// device (major, minor)
 | 
			
		||||
    dev: (usize, usize),
 | 
			
		||||
    /// inode on the device. 0 indicates that no inode is associated with the memory region (e.g. uninitalized data aka BSS).
 | 
			
		||||
    inode: usize,
 | 
			
		||||
    /// Usually the file backing the mapping.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note: The man page for proc includes a note about "coordination" by
 | 
			
		||||
    /// using readelf to see the Offset field in ELF program headers. pnkfelix
 | 
			
		||||
    /// is not yet sure if that is intended to be a comment on pathname, or what
 | 
			
		||||
    /// form/purpose such coordination is meant to have.
 | 
			
		||||
    ///
 | 
			
		||||
    /// There are also some pseudo-paths:
 | 
			
		||||
    /// "[stack]": The initial process's (aka main thread's) stack.
 | 
			
		||||
    /// "[stack:<tid>]": a specific thread's stack. (This was only present for a limited range of Linux verisons; it was determined to be too expensive to provide.)
 | 
			
		||||
    /// "[vdso]": Virtual dynamically linked shared object
 | 
			
		||||
    /// "[heap]": The process's heap
 | 
			
		||||
    ///
 | 
			
		||||
    /// The pathname can be blank, which means it is an anonymous mapping
 | 
			
		||||
    /// obtained via mmap.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Newlines in pathname are replaced with an octal escape sequence.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The pathname may have "(deleted)" appended onto it if the file-backed
 | 
			
		||||
    /// path has been deleted.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that modifications like the latter two indicated above imply that
 | 
			
		||||
    /// in general the pathname may be ambiguous. (I.e. you cannot tell if the
 | 
			
		||||
    /// denoted filename actually ended with the text "(deleted)", or if that
 | 
			
		||||
    /// was added by the maps rendering.
 | 
			
		||||
    pathname: OsString,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(super) fn parse_maps() -> Result<Vec<MapsEntry>, &'static str> {
 | 
			
		||||
    let mut v = Vec::new();
 | 
			
		||||
    let mut proc_self_maps =
 | 
			
		||||
        File::open("/proc/self/maps").map_err(|_| "Couldn't open /proc/self/maps")?;
 | 
			
		||||
    let mut buf = String::new();
 | 
			
		||||
    let _bytes_read = proc_self_maps
 | 
			
		||||
        .read_to_string(&mut buf)
 | 
			
		||||
        .map_err(|_| "Couldn't read /proc/self/maps")?;
 | 
			
		||||
    for line in buf.lines() {
 | 
			
		||||
        v.push(line.parse()?);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MapsEntry {
 | 
			
		||||
    pub(super) fn pathname(&self) -> &OsString {
 | 
			
		||||
        &self.pathname
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn ip_matches(&self, ip: usize) -> bool {
 | 
			
		||||
        self.address.0 <= ip && ip < self.address.1
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromStr for MapsEntry {
 | 
			
		||||
    type Err = &'static str;
 | 
			
		||||
 | 
			
		||||
    // Format: address perms offset dev inode pathname
 | 
			
		||||
    // e.g.: "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]"
 | 
			
		||||
    // e.g.: "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795                  /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
 | 
			
		||||
    // e.g.: "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
 | 
			
		||||
    //
 | 
			
		||||
    // Note that paths may contain spaces, so we can't use `str::split` for parsing (until
 | 
			
		||||
    // Split::remainder is stabilized #77998).
 | 
			
		||||
    fn from_str(s: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        let (range_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
 | 
			
		||||
        if range_str.is_empty() {
 | 
			
		||||
            return Err("Couldn't find address");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (perms_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
 | 
			
		||||
        if perms_str.is_empty() {
 | 
			
		||||
            return Err("Couldn't find permissions");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (offset_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
 | 
			
		||||
        if offset_str.is_empty() {
 | 
			
		||||
            return Err("Couldn't find offset");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (dev_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
 | 
			
		||||
        if dev_str.is_empty() {
 | 
			
		||||
            return Err("Couldn't find dev");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (inode_str, s) = s.trim_start().split_once(' ').unwrap_or((s, ""));
 | 
			
		||||
        if inode_str.is_empty() {
 | 
			
		||||
            return Err("Couldn't find inode");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Pathname may be omitted in which case it will be empty
 | 
			
		||||
        let pathname_str = s.trim_start();
 | 
			
		||||
 | 
			
		||||
        let hex = |s| usize::from_str_radix(s, 16).map_err(|_| "Couldn't parse hex number");
 | 
			
		||||
        let address = if let Some((start, limit)) = range_str.split_once('-') {
 | 
			
		||||
            (hex(start)?, hex(limit)?)
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err("Couldn't parse address range");
 | 
			
		||||
        };
 | 
			
		||||
        let perms: [char; 4] = {
 | 
			
		||||
            let mut chars = perms_str.chars();
 | 
			
		||||
            let mut c = || chars.next().ok_or("insufficient perms");
 | 
			
		||||
            let perms = [c()?, c()?, c()?, c()?];
 | 
			
		||||
            if chars.next().is_some() {
 | 
			
		||||
                return Err("too many perms");
 | 
			
		||||
            }
 | 
			
		||||
            perms
 | 
			
		||||
        };
 | 
			
		||||
        let offset = hex(offset_str)?;
 | 
			
		||||
        let dev = if let Some((major, minor)) = dev_str.split_once(':') {
 | 
			
		||||
            (hex(major)?, hex(minor)?)
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err("Couldn't parse dev");
 | 
			
		||||
        };
 | 
			
		||||
        let inode = hex(inode_str)?;
 | 
			
		||||
        let pathname = pathname_str.into();
 | 
			
		||||
 | 
			
		||||
        Ok(MapsEntry {
 | 
			
		||||
            address,
 | 
			
		||||
            perms,
 | 
			
		||||
            offset,
 | 
			
		||||
            dev,
 | 
			
		||||
            inode,
 | 
			
		||||
            pathname,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Make sure we can parse 64-bit sample output if we're on a 64-bit target.
 | 
			
		||||
#[cfg(target_pointer_width = "64")]
 | 
			
		||||
#[test]
 | 
			
		||||
fn check_maps_entry_parsing_64bit() {
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  \
 | 
			
		||||
                [vsyscall]"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0xffffffffff600000, 0xffffffffff601000),
 | 
			
		||||
            perms: ['-', '-', 'x', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x00, 0x00),
 | 
			
		||||
            inode: 0x0,
 | 
			
		||||
            pathname: "[vsyscall]".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "7f5985f46000-7f5985f48000 rw-p 00039000 103:06 76021795                  \
 | 
			
		||||
                /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0x7f5985f46000, 0x7f5985f48000),
 | 
			
		||||
            perms: ['r', 'w', '-', 'p'],
 | 
			
		||||
            offset: 0x00039000,
 | 
			
		||||
            dev: (0x103, 0x06),
 | 
			
		||||
            inode: 0x76021795,
 | 
			
		||||
            pathname: "/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "35b1a21000-35b1a22000 rw-p 00000000 00:00 0"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0x35b1a21000, 0x35b1a22000),
 | 
			
		||||
            perms: ['r', 'w', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x00, 0x00),
 | 
			
		||||
            inode: 0x0,
 | 
			
		||||
            pathname: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// (This output was taken from a 32-bit machine, but will work on any target)
 | 
			
		||||
#[test]
 | 
			
		||||
fn check_maps_entry_parsing_32bit() {
 | 
			
		||||
    /* Example snippet of output:
 | 
			
		||||
    08056000-08077000 rw-p 00000000 00:00 0          [heap]
 | 
			
		||||
    b7c79000-b7e02000 r--p 00000000 08:01 60662705   /usr/lib/locale/locale-archive
 | 
			
		||||
    b7e02000-b7e03000 rw-p 00000000 00:00 0
 | 
			
		||||
        */
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "08056000-08077000 rw-p 00000000 00:00 0          \
 | 
			
		||||
                [heap]"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0x08056000, 0x08077000),
 | 
			
		||||
            perms: ['r', 'w', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x00, 0x00),
 | 
			
		||||
            inode: 0x0,
 | 
			
		||||
            pathname: "[heap]".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "b7c79000-b7e02000 r--p 00000000 08:01 60662705   \
 | 
			
		||||
                /usr/lib/locale/locale-archive"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0xb7c79000, 0xb7e02000),
 | 
			
		||||
            perms: ['r', '-', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x08, 0x01),
 | 
			
		||||
            inode: 0x60662705,
 | 
			
		||||
            pathname: "/usr/lib/locale/locale-archive".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "b7e02000-b7e03000 rw-p 00000000 00:00 0"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0xb7e02000, 0xb7e03000),
 | 
			
		||||
            perms: ['r', 'w', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x00, 0x00),
 | 
			
		||||
            inode: 0x0,
 | 
			
		||||
            pathname: Default::default(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "b7c79000-b7e02000 r--p 00000000 08:01 60662705   \
 | 
			
		||||
                /executable/path/with some spaces"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0xb7c79000, 0xb7e02000),
 | 
			
		||||
            perms: ['r', '-', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x08, 0x01),
 | 
			
		||||
            inode: 0x60662705,
 | 
			
		||||
            pathname: "/executable/path/with some spaces".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "b7c79000-b7e02000 r--p 00000000 08:01 60662705   \
 | 
			
		||||
                /executable/path/with  multiple-continuous    spaces  "
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0xb7c79000, 0xb7e02000),
 | 
			
		||||
            perms: ['r', '-', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x08, 0x01),
 | 
			
		||||
            inode: 0x60662705,
 | 
			
		||||
            pathname: "/executable/path/with  multiple-continuous    spaces  ".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        "  b7c79000-b7e02000  r--p  00000000  08:01  60662705   \
 | 
			
		||||
                /executable/path/starts-with-spaces"
 | 
			
		||||
            .parse::<MapsEntry>()
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        MapsEntry {
 | 
			
		||||
            address: (0xb7c79000, 0xb7e02000),
 | 
			
		||||
            perms: ['r', '-', '-', 'p'],
 | 
			
		||||
            offset: 0x00000000,
 | 
			
		||||
            dev: (0x08, 0x01),
 | 
			
		||||
            inode: 0x60662705,
 | 
			
		||||
            pathname: "/executable/path/starts-with-spaces".into(),
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								vendor/backtrace/src/symbolize/gimli/stash.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/backtrace/src/symbolize/gimli/stash.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
// only used on Linux right now, so allow dead code elsewhere
 | 
			
		||||
#![cfg_attr(not(target_os = "linux"), allow(dead_code))]
 | 
			
		||||
 | 
			
		||||
use super::Mmap;
 | 
			
		||||
use alloc::vec;
 | 
			
		||||
use alloc::vec::Vec;
 | 
			
		||||
use core::cell::UnsafeCell;
 | 
			
		||||
 | 
			
		||||
/// A simple arena allocator for byte buffers.
 | 
			
		||||
pub struct Stash {
 | 
			
		||||
    buffers: UnsafeCell<Vec<Vec<u8>>>,
 | 
			
		||||
    mmaps: UnsafeCell<Vec<Mmap>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Stash {
 | 
			
		||||
    pub fn new() -> Stash {
 | 
			
		||||
        Stash {
 | 
			
		||||
            buffers: UnsafeCell::new(Vec::new()),
 | 
			
		||||
            mmaps: UnsafeCell::new(Vec::new()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Allocates a buffer of the specified size and returns a mutable reference
 | 
			
		||||
    /// to it.
 | 
			
		||||
    pub fn allocate(&self, size: usize) -> &mut [u8] {
 | 
			
		||||
        // SAFETY: this is the only function that ever constructs a mutable
 | 
			
		||||
        // reference to `self.buffers`.
 | 
			
		||||
        let buffers = unsafe { &mut *self.buffers.get() };
 | 
			
		||||
        let i = buffers.len();
 | 
			
		||||
        buffers.push(vec![0; size]);
 | 
			
		||||
        // SAFETY: we never remove elements from `self.buffers`, so a reference
 | 
			
		||||
        // to the data inside any buffer will live as long as `self` does.
 | 
			
		||||
        &mut buffers[i]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Stores a `Mmap` for the lifetime of this `Stash`, returning a pointer
 | 
			
		||||
    /// which is scoped to just this lifetime.
 | 
			
		||||
    pub fn cache_mmap(&self, map: Mmap) -> &[u8] {
 | 
			
		||||
        // SAFETY: this is the only location for a mutable pointer to
 | 
			
		||||
        // `mmaps`, and this structure isn't threadsafe to shared across
 | 
			
		||||
        // threads either. We also never remove elements from `self.mmaps`,
 | 
			
		||||
        // so a reference to the data inside the map will live as long as
 | 
			
		||||
        // `self` does.
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let mmaps = &mut *self.mmaps.get();
 | 
			
		||||
            mmaps.push(map);
 | 
			
		||||
            mmaps.last().unwrap()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								vendor/backtrace/src/symbolize/miri.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								vendor/backtrace/src/symbolize/miri.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
 | 
			
		||||
use super::super::backtrace::miri::{resolve_addr, Frame};
 | 
			
		||||
use super::BytesOrWideString;
 | 
			
		||||
use super::{ResolveWhat, SymbolName};
 | 
			
		||||
 | 
			
		||||
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
 | 
			
		||||
    let sym = match what {
 | 
			
		||||
        ResolveWhat::Address(addr) => Symbol {
 | 
			
		||||
            inner: resolve_addr(addr),
 | 
			
		||||
            _unused: PhantomData,
 | 
			
		||||
        },
 | 
			
		||||
        ResolveWhat::Frame(frame) => Symbol {
 | 
			
		||||
            inner: frame.inner.clone(),
 | 
			
		||||
            _unused: PhantomData,
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
    cb(&super::Symbol { inner: sym })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Symbol<'a> {
 | 
			
		||||
    inner: Frame,
 | 
			
		||||
    _unused: PhantomData<&'a ()>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Symbol<'a> {
 | 
			
		||||
    pub fn name(&self) -> Option<SymbolName<'_>> {
 | 
			
		||||
        Some(SymbolName::new(&self.inner.inner.name))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn addr(&self) -> Option<*mut c_void> {
 | 
			
		||||
        Some(self.inner.addr)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
 | 
			
		||||
        Some(BytesOrWideString::Bytes(&self.inner.inner.filename))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lineno(&self) -> Option<u32> {
 | 
			
		||||
        Some(self.inner.inner.lineno)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn colno(&self) -> Option<u32> {
 | 
			
		||||
        Some(self.inner.inner.colno)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "std")]
 | 
			
		||||
    pub fn filename(&self) -> Option<&std::path::Path> {
 | 
			
		||||
        Some(std::path::Path::new(
 | 
			
		||||
            core::str::from_utf8(&self.inner.inner.filename).unwrap(),
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub unsafe fn clear_symbol_cache() {}
 | 
			
		||||
							
								
								
									
										485
									
								
								vendor/backtrace/src/symbolize/mod.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										485
									
								
								vendor/backtrace/src/symbolize/mod.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,485 @@
 | 
			
		||||
use core::{fmt, str};
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(feature = "std")] {
 | 
			
		||||
        use std::path::Path;
 | 
			
		||||
        use std::prelude::v1::*;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use super::backtrace::Frame;
 | 
			
		||||
use super::types::BytesOrWideString;
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
use rustc_demangle::{try_demangle, Demangle};
 | 
			
		||||
 | 
			
		||||
/// Resolve an address to a symbol, passing the symbol to the specified
 | 
			
		||||
/// closure.
 | 
			
		||||
///
 | 
			
		||||
/// This function will look up the given address in areas such as the local
 | 
			
		||||
/// symbol table, dynamic symbol table, or DWARF debug info (depending on the
 | 
			
		||||
/// activated implementation) to find symbols to yield.
 | 
			
		||||
///
 | 
			
		||||
/// The closure may not be called if resolution could not be performed, and it
 | 
			
		||||
/// also may be called more than once in the case of inlined functions.
 | 
			
		||||
///
 | 
			
		||||
/// Symbols yielded represent the execution at the specified `addr`, returning
 | 
			
		||||
/// file/line pairs for that address (if available).
 | 
			
		||||
///
 | 
			
		||||
/// Note that if you have a `Frame` then it's recommended to use the
 | 
			
		||||
/// `resolve_frame` function instead of this one.
 | 
			
		||||
///
 | 
			
		||||
/// # Required features
 | 
			
		||||
///
 | 
			
		||||
/// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
/// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
///
 | 
			
		||||
/// # Panics
 | 
			
		||||
///
 | 
			
		||||
/// This function strives to never panic, but if the `cb` provided panics then
 | 
			
		||||
/// some platforms will force a double panic to abort the process. Some
 | 
			
		||||
/// platforms use a C library which internally uses callbacks which cannot be
 | 
			
		||||
/// unwound through, so panicking from `cb` may trigger a process abort.
 | 
			
		||||
///
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// extern crate backtrace;
 | 
			
		||||
///
 | 
			
		||||
/// fn main() {
 | 
			
		||||
///     backtrace::trace(|frame| {
 | 
			
		||||
///         let ip = frame.ip();
 | 
			
		||||
///
 | 
			
		||||
///         backtrace::resolve(ip, |symbol| {
 | 
			
		||||
///             // ...
 | 
			
		||||
///         });
 | 
			
		||||
///
 | 
			
		||||
///         false // only look at the top frame
 | 
			
		||||
///     });
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub fn resolve<F: FnMut(&Symbol)>(addr: *mut c_void, cb: F) {
 | 
			
		||||
    let _guard = crate::lock::lock();
 | 
			
		||||
    unsafe { resolve_unsynchronized(addr, cb) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Resolve a previously capture frame to a symbol, passing the symbol to the
 | 
			
		||||
/// specified closure.
 | 
			
		||||
///
 | 
			
		||||
/// This function performs the same function as `resolve` except that it takes a
 | 
			
		||||
/// `Frame` as an argument instead of an address. This can allow some platform
 | 
			
		||||
/// implementations of backtracing to provide more accurate symbol information
 | 
			
		||||
/// or information about inline frames for example. It's recommended to use this
 | 
			
		||||
/// if you can.
 | 
			
		||||
///
 | 
			
		||||
/// # Required features
 | 
			
		||||
///
 | 
			
		||||
/// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
/// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
///
 | 
			
		||||
/// # Panics
 | 
			
		||||
///
 | 
			
		||||
/// This function strives to never panic, but if the `cb` provided panics then
 | 
			
		||||
/// some platforms will force a double panic to abort the process. Some
 | 
			
		||||
/// platforms use a C library which internally uses callbacks which cannot be
 | 
			
		||||
/// unwound through, so panicking from `cb` may trigger a process abort.
 | 
			
		||||
///
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// extern crate backtrace;
 | 
			
		||||
///
 | 
			
		||||
/// fn main() {
 | 
			
		||||
///     backtrace::trace(|frame| {
 | 
			
		||||
///         backtrace::resolve_frame(frame, |symbol| {
 | 
			
		||||
///             // ...
 | 
			
		||||
///         });
 | 
			
		||||
///
 | 
			
		||||
///         false // only look at the top frame
 | 
			
		||||
///     });
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub fn resolve_frame<F: FnMut(&Symbol)>(frame: &Frame, cb: F) {
 | 
			
		||||
    let _guard = crate::lock::lock();
 | 
			
		||||
    unsafe { resolve_frame_unsynchronized(frame, cb) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum ResolveWhat<'a> {
 | 
			
		||||
    Address(*mut c_void),
 | 
			
		||||
    Frame(&'a Frame),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> ResolveWhat<'a> {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    fn address_or_ip(&self) -> *mut c_void {
 | 
			
		||||
        match self {
 | 
			
		||||
            ResolveWhat::Address(a) => adjust_ip(*a),
 | 
			
		||||
            ResolveWhat::Frame(f) => adjust_ip(f.ip()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IP values from stack frames are typically (always?) the instruction
 | 
			
		||||
// *after* the call that's the actual stack trace. Symbolizing this on
 | 
			
		||||
// causes the filename/line number to be one ahead and perhaps into
 | 
			
		||||
// the void if it's near the end of the function.
 | 
			
		||||
//
 | 
			
		||||
// This appears to basically always be the case on all platforms, so we always
 | 
			
		||||
// subtract one from a resolved ip to resolve it to the previous call
 | 
			
		||||
// instruction instead of the instruction being returned to.
 | 
			
		||||
//
 | 
			
		||||
// Ideally we would not do this. Ideally we would require callers of the
 | 
			
		||||
// `resolve` APIs here to manually do the -1 and account that they want location
 | 
			
		||||
// information for the *previous* instruction, not the current. Ideally we'd
 | 
			
		||||
// also expose on `Frame` if we are indeed the address of the next instruction
 | 
			
		||||
// or the current.
 | 
			
		||||
//
 | 
			
		||||
// For now though this is a pretty niche concern so we just internally always
 | 
			
		||||
// subtract one. Consumers should keep working and getting pretty good results,
 | 
			
		||||
// so we should be good enough.
 | 
			
		||||
fn adjust_ip(a: *mut c_void) -> *mut c_void {
 | 
			
		||||
    if a.is_null() {
 | 
			
		||||
        a
 | 
			
		||||
    } else {
 | 
			
		||||
        (a as usize - 1) as *mut c_void
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Same as `resolve`, only unsafe as it's unsynchronized.
 | 
			
		||||
///
 | 
			
		||||
/// This function does not have synchronization guarantees but is available when
 | 
			
		||||
/// the `std` feature of this crate isn't compiled in. See the `resolve`
 | 
			
		||||
/// function for more documentation and examples.
 | 
			
		||||
///
 | 
			
		||||
/// # Panics
 | 
			
		||||
///
 | 
			
		||||
/// See information on `resolve` for caveats on `cb` panicking.
 | 
			
		||||
pub unsafe fn resolve_unsynchronized<F>(addr: *mut c_void, mut cb: F)
 | 
			
		||||
where
 | 
			
		||||
    F: FnMut(&Symbol),
 | 
			
		||||
{
 | 
			
		||||
    imp::resolve(ResolveWhat::Address(addr), &mut cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Same as `resolve_frame`, only unsafe as it's unsynchronized.
 | 
			
		||||
///
 | 
			
		||||
/// This function does not have synchronization guarantees but is available
 | 
			
		||||
/// when the `std` feature of this crate isn't compiled in. See the
 | 
			
		||||
/// `resolve_frame` function for more documentation and examples.
 | 
			
		||||
///
 | 
			
		||||
/// # Panics
 | 
			
		||||
///
 | 
			
		||||
/// See information on `resolve_frame` for caveats on `cb` panicking.
 | 
			
		||||
pub unsafe fn resolve_frame_unsynchronized<F>(frame: &Frame, mut cb: F)
 | 
			
		||||
where
 | 
			
		||||
    F: FnMut(&Symbol),
 | 
			
		||||
{
 | 
			
		||||
    imp::resolve(ResolveWhat::Frame(frame), &mut cb)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A trait representing the resolution of a symbol in a file.
 | 
			
		||||
///
 | 
			
		||||
/// This trait is yielded as a trait object to the closure given to the
 | 
			
		||||
/// `backtrace::resolve` function, and it is virtually dispatched as it's
 | 
			
		||||
/// unknown which implementation is behind it.
 | 
			
		||||
///
 | 
			
		||||
/// A symbol can give contextual information about a function, for example the
 | 
			
		||||
/// name, filename, line number, precise address, etc. Not all information is
 | 
			
		||||
/// always available in a symbol, however, so all methods return an `Option`.
 | 
			
		||||
pub struct Symbol {
 | 
			
		||||
    // TODO: this lifetime bound needs to be persisted eventually to `Symbol`,
 | 
			
		||||
    // but that's currently a breaking change. For now this is safe since
 | 
			
		||||
    // `Symbol` is only ever handed out by reference and can't be cloned.
 | 
			
		||||
    inner: imp::Symbol<'static>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Symbol {
 | 
			
		||||
    /// Returns the name of this function.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The returned structure can be used to query various properties about the
 | 
			
		||||
    /// symbol name:
 | 
			
		||||
    ///
 | 
			
		||||
    /// * The `Display` implementation will print out the demangled symbol.
 | 
			
		||||
    /// * The raw `str` value of the symbol can be accessed (if it's valid
 | 
			
		||||
    ///   utf-8).
 | 
			
		||||
    /// * The raw bytes for the symbol name can be accessed.
 | 
			
		||||
    pub fn name(&self) -> Option<SymbolName<'_>> {
 | 
			
		||||
        self.inner.name()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the starting address of this function.
 | 
			
		||||
    pub fn addr(&self) -> Option<*mut c_void> {
 | 
			
		||||
        self.inner.addr().map(|p| p as *mut _)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the raw filename as a slice. This is mainly useful for `no_std`
 | 
			
		||||
    /// environments.
 | 
			
		||||
    pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
 | 
			
		||||
        self.inner.filename_raw()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the column number for where this symbol is currently executing.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Only gimli currently provides a value here and even then only if `filename`
 | 
			
		||||
    /// returns `Some`, and so it is then consequently subject to similar caveats.
 | 
			
		||||
    pub fn colno(&self) -> Option<u32> {
 | 
			
		||||
        self.inner.colno()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the line number for where this symbol is currently executing.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This return value is typically `Some` if `filename` returns `Some`, and
 | 
			
		||||
    /// is consequently subject to similar caveats.
 | 
			
		||||
    pub fn lineno(&self) -> Option<u32> {
 | 
			
		||||
        self.inner.lineno()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the file name where this function was defined.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is currently only available when libbacktrace or gimli is being
 | 
			
		||||
    /// used (e.g. unix platforms other) and when a binary is compiled with
 | 
			
		||||
    /// debuginfo. If neither of these conditions is met then this will likely
 | 
			
		||||
    /// return `None`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # 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")]
 | 
			
		||||
    #[allow(unreachable_code)]
 | 
			
		||||
    pub fn filename(&self) -> Option<&Path> {
 | 
			
		||||
        self.inner.filename()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Symbol {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let mut d = f.debug_struct("Symbol");
 | 
			
		||||
        if let Some(name) = self.name() {
 | 
			
		||||
            d.field("name", &name);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(addr) = self.addr() {
 | 
			
		||||
            d.field("addr", &addr);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "std")]
 | 
			
		||||
        {
 | 
			
		||||
            if let Some(filename) = self.filename() {
 | 
			
		||||
                d.field("filename", &filename);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(lineno) = self.lineno() {
 | 
			
		||||
            d.field("lineno", &lineno);
 | 
			
		||||
        }
 | 
			
		||||
        d.finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(feature = "cpp_demangle")] {
 | 
			
		||||
        // Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust
 | 
			
		||||
        // failed.
 | 
			
		||||
        struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>);
 | 
			
		||||
 | 
			
		||||
        impl<'a> OptionCppSymbol<'a> {
 | 
			
		||||
            fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> {
 | 
			
		||||
                OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn none() -> OptionCppSymbol<'a> {
 | 
			
		||||
                OptionCppSymbol(None)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        use core::marker::PhantomData;
 | 
			
		||||
 | 
			
		||||
        // Make sure to keep this zero-sized, so that the `cpp_demangle` feature
 | 
			
		||||
        // has no cost when disabled.
 | 
			
		||||
        struct OptionCppSymbol<'a>(PhantomData<&'a ()>);
 | 
			
		||||
 | 
			
		||||
        impl<'a> OptionCppSymbol<'a> {
 | 
			
		||||
            fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> {
 | 
			
		||||
                OptionCppSymbol(PhantomData)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn none() -> OptionCppSymbol<'a> {
 | 
			
		||||
                OptionCppSymbol(PhantomData)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A wrapper around a symbol name to provide ergonomic accessors to the
 | 
			
		||||
/// demangled name, the raw bytes, the raw string, etc.
 | 
			
		||||
// Allow dead code for when the `cpp_demangle` feature is not enabled.
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
pub struct SymbolName<'a> {
 | 
			
		||||
    bytes: &'a [u8],
 | 
			
		||||
    demangled: Option<Demangle<'a>>,
 | 
			
		||||
    cpp_demangled: OptionCppSymbol<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> SymbolName<'a> {
 | 
			
		||||
    /// Creates a new symbol name from the raw underlying bytes.
 | 
			
		||||
    pub fn new(bytes: &'a [u8]) -> SymbolName<'a> {
 | 
			
		||||
        let str_bytes = str::from_utf8(bytes).ok();
 | 
			
		||||
        let demangled = str_bytes.and_then(|s| try_demangle(s).ok());
 | 
			
		||||
 | 
			
		||||
        let cpp = if demangled.is_none() {
 | 
			
		||||
            OptionCppSymbol::parse(bytes)
 | 
			
		||||
        } else {
 | 
			
		||||
            OptionCppSymbol::none()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        SymbolName {
 | 
			
		||||
            bytes: bytes,
 | 
			
		||||
            demangled: demangled,
 | 
			
		||||
            cpp_demangled: cpp,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Use the `Display` implementation if you want the demangled version.
 | 
			
		||||
    pub fn as_str(&self) -> Option<&'a str> {
 | 
			
		||||
        self.demangled
 | 
			
		||||
            .as_ref()
 | 
			
		||||
            .map(|s| s.as_str())
 | 
			
		||||
            .or_else(|| str::from_utf8(self.bytes).ok())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the raw symbol name as a list of bytes
 | 
			
		||||
    pub fn as_bytes(&self) -> &'a [u8] {
 | 
			
		||||
        self.bytes
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn format_symbol_name(
 | 
			
		||||
    fmt: fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result,
 | 
			
		||||
    mut bytes: &[u8],
 | 
			
		||||
    f: &mut fmt::Formatter<'_>,
 | 
			
		||||
) -> fmt::Result {
 | 
			
		||||
    while bytes.len() > 0 {
 | 
			
		||||
        match str::from_utf8(bytes) {
 | 
			
		||||
            Ok(name) => {
 | 
			
		||||
                fmt(name, f)?;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                fmt("\u{FFFD}", f)?;
 | 
			
		||||
 | 
			
		||||
                match err.error_len() {
 | 
			
		||||
                    Some(len) => bytes = &bytes[err.valid_up_to() + len..],
 | 
			
		||||
                    None => break,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(feature = "cpp_demangle")] {
 | 
			
		||||
        impl<'a> fmt::Display for SymbolName<'a> {
 | 
			
		||||
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
                if let Some(ref s) = self.demangled {
 | 
			
		||||
                    s.fmt(f)
 | 
			
		||||
                } else if let Some(ref cpp) = self.cpp_demangled.0 {
 | 
			
		||||
                    cpp.fmt(f)
 | 
			
		||||
                } else {
 | 
			
		||||
                    format_symbol_name(fmt::Display::fmt, self.bytes, f)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        impl<'a> fmt::Display for SymbolName<'a> {
 | 
			
		||||
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
                if let Some(ref s) = self.demangled {
 | 
			
		||||
                    s.fmt(f)
 | 
			
		||||
                } else {
 | 
			
		||||
                    format_symbol_name(fmt::Display::fmt, self.bytes, f)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(all(feature = "std", feature = "cpp_demangle"))] {
 | 
			
		||||
        impl<'a> fmt::Debug for SymbolName<'a> {
 | 
			
		||||
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
                use std::fmt::Write;
 | 
			
		||||
 | 
			
		||||
                if let Some(ref s) = self.demangled {
 | 
			
		||||
                    return s.fmt(f)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // This may to print if the demangled symbol isn't actually
 | 
			
		||||
                // valid, so handle the error here gracefully by not propagating
 | 
			
		||||
                // it outwards.
 | 
			
		||||
                if let Some(ref cpp) = self.cpp_demangled.0 {
 | 
			
		||||
                    let mut s = String::new();
 | 
			
		||||
                    if write!(s, "{}", cpp).is_ok() {
 | 
			
		||||
                        return s.fmt(f)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                format_symbol_name(fmt::Debug::fmt, self.bytes, f)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        impl<'a> fmt::Debug for SymbolName<'a> {
 | 
			
		||||
            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
                if let Some(ref s) = self.demangled {
 | 
			
		||||
                    s.fmt(f)
 | 
			
		||||
                } else {
 | 
			
		||||
                    format_symbol_name(fmt::Debug::fmt, self.bytes, f)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Attempt to reclaim that cached memory used to symbolicate addresses.
 | 
			
		||||
///
 | 
			
		||||
/// This method will attempt to release any global data structures that have
 | 
			
		||||
/// otherwise been cached globally or in the thread which typically represent
 | 
			
		||||
/// parsed DWARF information or similar.
 | 
			
		||||
///
 | 
			
		||||
/// # Caveats
 | 
			
		||||
///
 | 
			
		||||
/// While this function is always available it doesn't actually do anything on
 | 
			
		||||
/// most implementations. Libraries like dbghelp or libbacktrace do not provide
 | 
			
		||||
/// facilities to deallocate state and manage the allocated memory. For now the
 | 
			
		||||
/// `gimli-symbolize` feature of this crate is the only feature where this
 | 
			
		||||
/// function has any effect.
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
pub fn clear_symbol_cache() {
 | 
			
		||||
    let _guard = crate::lock::lock();
 | 
			
		||||
    unsafe {
 | 
			
		||||
        imp::clear_symbol_cache();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(miri)] {
 | 
			
		||||
        mod miri;
 | 
			
		||||
        use miri as imp;
 | 
			
		||||
    } else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] {
 | 
			
		||||
        mod dbghelp;
 | 
			
		||||
        use dbghelp as imp;
 | 
			
		||||
    } else if #[cfg(all(
 | 
			
		||||
        any(unix, all(windows, target_env = "gnu")),
 | 
			
		||||
        not(target_vendor = "uwp"),
 | 
			
		||||
        not(target_os = "emscripten"),
 | 
			
		||||
        any(not(backtrace_in_libstd), feature = "backtrace"),
 | 
			
		||||
    ))] {
 | 
			
		||||
        mod gimli;
 | 
			
		||||
        use gimli as imp;
 | 
			
		||||
    } else {
 | 
			
		||||
        mod noop;
 | 
			
		||||
        use noop as imp;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/backtrace/src/symbolize/noop.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/backtrace/src/symbolize/noop.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
//! Empty symbolication strategy used to compile for platforms that have no
 | 
			
		||||
//! support.
 | 
			
		||||
 | 
			
		||||
use super::{BytesOrWideString, ResolveWhat, SymbolName};
 | 
			
		||||
use core::ffi::c_void;
 | 
			
		||||
use core::marker;
 | 
			
		||||
 | 
			
		||||
pub unsafe fn resolve(_addr: ResolveWhat<'_>, _cb: &mut dyn FnMut(&super::Symbol)) {}
 | 
			
		||||
 | 
			
		||||
pub struct Symbol<'a> {
 | 
			
		||||
    _marker: marker::PhantomData<&'a i32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Symbol<'_> {
 | 
			
		||||
    pub fn name(&self) -> Option<SymbolName<'_>> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn addr(&self) -> Option<*mut c_void> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "std")]
 | 
			
		||||
    pub fn filename(&self) -> Option<&::std::path::Path> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lineno(&self) -> Option<u32> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn colno(&self) -> Option<u32> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub unsafe fn clear_symbol_cache() {}
 | 
			
		||||
							
								
								
									
										83
									
								
								vendor/backtrace/src/types.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/backtrace/src/types.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
//! Platform dependent types.
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(feature = "std")] {
 | 
			
		||||
        use std::borrow::Cow;
 | 
			
		||||
        use std::fmt;
 | 
			
		||||
        use std::path::PathBuf;
 | 
			
		||||
        use std::prelude::v1::*;
 | 
			
		||||
        use std::str;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A platform independent representation of a string. When working with `std`
 | 
			
		||||
/// enabled it is recommended to the convenience methods for providing
 | 
			
		||||
/// conversions to `std` types.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum BytesOrWideString<'a> {
 | 
			
		||||
    /// A slice, typically provided on Unix platforms.
 | 
			
		||||
    Bytes(&'a [u8]),
 | 
			
		||||
    /// Wide strings typically from Windows.
 | 
			
		||||
    Wide(&'a [u16]),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
impl<'a> BytesOrWideString<'a> {
 | 
			
		||||
    /// Lossy converts to a `Cow<str>`, will allocate if `Bytes` is not valid
 | 
			
		||||
    /// UTF-8 or if `BytesOrWideString` is `Wide`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn to_str_lossy(&self) -> Cow<'a, str> {
 | 
			
		||||
        use self::BytesOrWideString::*;
 | 
			
		||||
 | 
			
		||||
        match self {
 | 
			
		||||
            &Bytes(slice) => String::from_utf8_lossy(slice),
 | 
			
		||||
            &Wide(wide) => Cow::Owned(String::from_utf16_lossy(wide)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Provides a `Path` representation of `BytesOrWideString`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Required features
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function requires the `std` feature of the `backtrace` crate to be
 | 
			
		||||
    /// enabled, and the `std` feature is enabled by default.
 | 
			
		||||
    pub fn into_path_buf(self) -> PathBuf {
 | 
			
		||||
        #[cfg(unix)]
 | 
			
		||||
        {
 | 
			
		||||
            use std::ffi::OsStr;
 | 
			
		||||
            use std::os::unix::ffi::OsStrExt;
 | 
			
		||||
 | 
			
		||||
            if let BytesOrWideString::Bytes(slice) = self {
 | 
			
		||||
                return PathBuf::from(OsStr::from_bytes(slice));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[cfg(windows)]
 | 
			
		||||
        {
 | 
			
		||||
            use std::ffi::OsString;
 | 
			
		||||
            use std::os::windows::ffi::OsStringExt;
 | 
			
		||||
 | 
			
		||||
            if let BytesOrWideString::Wide(slice) = self {
 | 
			
		||||
                return PathBuf::from(OsString::from_wide(slice));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let BytesOrWideString::Bytes(b) = self {
 | 
			
		||||
            if let Ok(s) = str::from_utf8(b) {
 | 
			
		||||
                return PathBuf::from(s);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "std")]
 | 
			
		||||
impl<'a> fmt::Display for BytesOrWideString<'a> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.to_str_lossy().fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										693
									
								
								vendor/backtrace/src/windows.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										693
									
								
								vendor/backtrace/src/windows.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,693 @@
 | 
			
		||||
//! A module to define the FFI definitions we use on Windows for `dbghelp.dll`
 | 
			
		||||
//!
 | 
			
		||||
//! This module uses a custom macro, `ffi!`, to wrap all definitions to
 | 
			
		||||
//! automatically generate tests to assert that our definitions here are the
 | 
			
		||||
//! same as `winapi`.
 | 
			
		||||
//!
 | 
			
		||||
//! This module largely exists to integrate into libstd itself where winapi is
 | 
			
		||||
//! not currently available.
 | 
			
		||||
 | 
			
		||||
#![allow(bad_style, dead_code)]
 | 
			
		||||
 | 
			
		||||
cfg_if::cfg_if! {
 | 
			
		||||
    if #[cfg(feature = "verify-winapi")] {
 | 
			
		||||
        pub use self::winapi::c_void;
 | 
			
		||||
        pub use self::winapi::HINSTANCE;
 | 
			
		||||
        pub use self::winapi::FARPROC;
 | 
			
		||||
        pub use self::winapi::LPSECURITY_ATTRIBUTES;
 | 
			
		||||
        #[cfg(target_pointer_width = "64")]
 | 
			
		||||
        pub use self::winapi::PUNWIND_HISTORY_TABLE;
 | 
			
		||||
        #[cfg(target_pointer_width = "64")]
 | 
			
		||||
        pub use self::winapi::PRUNTIME_FUNCTION;
 | 
			
		||||
 | 
			
		||||
        mod winapi {
 | 
			
		||||
            pub use winapi::ctypes::*;
 | 
			
		||||
            pub use winapi::shared::basetsd::*;
 | 
			
		||||
            pub use winapi::shared::minwindef::*;
 | 
			
		||||
            pub use winapi::um::dbghelp::*;
 | 
			
		||||
            pub use winapi::um::fileapi::*;
 | 
			
		||||
            pub use winapi::um::handleapi::*;
 | 
			
		||||
            pub use winapi::um::libloaderapi::*;
 | 
			
		||||
            pub use winapi::um::memoryapi::*;
 | 
			
		||||
            pub use winapi::um::minwinbase::*;
 | 
			
		||||
            pub use winapi::um::processthreadsapi::*;
 | 
			
		||||
            pub use winapi::um::synchapi::*;
 | 
			
		||||
            pub use winapi::um::tlhelp32::*;
 | 
			
		||||
            pub use winapi::um::winbase::*;
 | 
			
		||||
            pub use winapi::um::winnt::*;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        pub use core::ffi::c_void;
 | 
			
		||||
        pub type HINSTANCE = *mut c_void;
 | 
			
		||||
        pub type FARPROC = *mut c_void;
 | 
			
		||||
        pub type LPSECURITY_ATTRIBUTES = *mut c_void;
 | 
			
		||||
        #[cfg(target_pointer_width = "64")]
 | 
			
		||||
        pub type PRUNTIME_FUNCTION = *mut c_void;
 | 
			
		||||
        #[cfg(target_pointer_width = "64")]
 | 
			
		||||
        pub type PUNWIND_HISTORY_TABLE = *mut c_void;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! ffi {
 | 
			
		||||
	() => ();
 | 
			
		||||
 | 
			
		||||
    (#[repr($($r:tt)*)] pub struct $name:ident { $(pub $field:ident: $ty:ty,)* } $($rest:tt)*) => (
 | 
			
		||||
        #[repr($($r)*)]
 | 
			
		||||
        #[cfg(not(feature = "verify-winapi"))]
 | 
			
		||||
        #[derive(Copy, Clone)]
 | 
			
		||||
        pub struct $name {
 | 
			
		||||
            $(pub $field: $ty,)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "verify-winapi")]
 | 
			
		||||
        pub use self::winapi::$name;
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg(feature = "verify-winapi")]
 | 
			
		||||
        fn $name() {
 | 
			
		||||
            use core::mem;
 | 
			
		||||
 | 
			
		||||
            #[repr($($r)*)]
 | 
			
		||||
            pub struct $name {
 | 
			
		||||
                $(pub $field: $ty,)*
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                mem::size_of::<$name>(),
 | 
			
		||||
                mem::size_of::<winapi::$name>(),
 | 
			
		||||
                concat!("size of ", stringify!($name), " is wrong"),
 | 
			
		||||
            );
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                mem::align_of::<$name>(),
 | 
			
		||||
                mem::align_of::<winapi::$name>(),
 | 
			
		||||
                concat!("align of ", stringify!($name), " is wrong"),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            type Winapi = winapi::$name;
 | 
			
		||||
 | 
			
		||||
            fn assert_same<T>(_: T, _: T) {}
 | 
			
		||||
 | 
			
		||||
            unsafe {
 | 
			
		||||
                let a = &*(mem::align_of::<$name>() as *const $name);
 | 
			
		||||
                let b = &*(mem::align_of::<Winapi>() as *const Winapi);
 | 
			
		||||
 | 
			
		||||
                $(
 | 
			
		||||
                    ffi!(@test_fields a b $field $ty);
 | 
			
		||||
                )*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ffi!($($rest)*);
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Handling verification against unions in winapi requires some special care
 | 
			
		||||
    (@test_fields $a:ident $b:ident FltSave $ty:ty) => (
 | 
			
		||||
        // Skip this field on x86_64 `CONTEXT` since it's a union and a bit funny
 | 
			
		||||
    );
 | 
			
		||||
    (@test_fields $a:ident $b:ident D $ty:ty) => ({
 | 
			
		||||
        let a = &$a.D;
 | 
			
		||||
        let b = $b.D();
 | 
			
		||||
        assert_same(a, b);
 | 
			
		||||
        assert_eq!(a as *const $ty, b as *const $ty, "misplaced field D");
 | 
			
		||||
    });
 | 
			
		||||
    (@test_fields $a:ident $b:ident s $ty:ty) => ({
 | 
			
		||||
        let a = &$a.s;
 | 
			
		||||
        let b = $b.s();
 | 
			
		||||
        assert_same(a, b);
 | 
			
		||||
        assert_eq!(a as *const $ty, b as *const $ty, "misplaced field s");
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Otherwise test all fields normally.
 | 
			
		||||
    (@test_fields $a:ident $b:ident $field:ident $ty:ty) => ({
 | 
			
		||||
        let a = &$a.$field;
 | 
			
		||||
        let b = &$b.$field;
 | 
			
		||||
        assert_same(a, b);
 | 
			
		||||
        assert_eq!(a as *const $ty, b as *const $ty,
 | 
			
		||||
                   concat!("misplaced field ", stringify!($field)));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    (pub type $name:ident = $ty:ty; $($rest:tt)*) => (
 | 
			
		||||
        pub type $name = $ty;
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "verify-winapi")]
 | 
			
		||||
        #[allow(dead_code)]
 | 
			
		||||
        const $name: () = {
 | 
			
		||||
            fn _foo() {
 | 
			
		||||
                trait SameType {}
 | 
			
		||||
                impl<T> SameType for (T, T) {}
 | 
			
		||||
                fn assert_same<T: SameType>() {}
 | 
			
		||||
 | 
			
		||||
                assert_same::<($name, winapi::$name)>();
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        ffi!($($rest)*);
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    (pub const $name:ident: $ty:ty = $val:expr; $($rest:tt)*) => (
 | 
			
		||||
        pub const $name: $ty = $val;
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "verify-winapi")]
 | 
			
		||||
        #[allow(unused_imports)]
 | 
			
		||||
        mod $name {
 | 
			
		||||
            use super::*;
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn assert_valid() {
 | 
			
		||||
                let x: $ty = winapi::$name;
 | 
			
		||||
                assert_eq!(x, $val);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        ffi!($($rest)*);
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    ($(#[$meta:meta])* extern "system" { $(pub fn $name:ident($($args:tt)*) -> $ret:ty;)* } $($rest:tt)*) => (
 | 
			
		||||
        $(#[$meta])* extern "system" {
 | 
			
		||||
            $(pub fn $name($($args)*) -> $ret;)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $(
 | 
			
		||||
            #[cfg(feature = "verify-winapi")]
 | 
			
		||||
            mod $name {
 | 
			
		||||
                #[test]
 | 
			
		||||
                fn assert_same() {
 | 
			
		||||
                    use super::*;
 | 
			
		||||
 | 
			
		||||
                    assert_eq!($name as usize, winapi::$name as usize);
 | 
			
		||||
                    let mut x: unsafe extern "system" fn($($args)*) -> $ret;
 | 
			
		||||
                    x = $name;
 | 
			
		||||
                    let _ = x;
 | 
			
		||||
                    x = winapi::$name;
 | 
			
		||||
                    let _ = x;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        )*
 | 
			
		||||
 | 
			
		||||
        ffi!($($rest)*);
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    (impl $name:ident { $($i:tt)* } $($rest:tt)*) => (
 | 
			
		||||
        #[cfg(not(feature = "verify-winapi"))]
 | 
			
		||||
        impl $name {
 | 
			
		||||
            $($i)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ffi!($($rest)*);
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ffi! {
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct STACKFRAME64 {
 | 
			
		||||
        pub AddrPC: ADDRESS64,
 | 
			
		||||
        pub AddrReturn: ADDRESS64,
 | 
			
		||||
        pub AddrFrame: ADDRESS64,
 | 
			
		||||
        pub AddrStack: ADDRESS64,
 | 
			
		||||
        pub AddrBStore: ADDRESS64,
 | 
			
		||||
        pub FuncTableEntry: PVOID,
 | 
			
		||||
        pub Params: [DWORD64; 4],
 | 
			
		||||
        pub Far: BOOL,
 | 
			
		||||
        pub Virtual: BOOL,
 | 
			
		||||
        pub Reserved: [DWORD64; 3],
 | 
			
		||||
        pub KdHelp: KDHELP64,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type LPSTACKFRAME64 = *mut STACKFRAME64;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct STACKFRAME_EX {
 | 
			
		||||
        pub AddrPC: ADDRESS64,
 | 
			
		||||
        pub AddrReturn: ADDRESS64,
 | 
			
		||||
        pub AddrFrame: ADDRESS64,
 | 
			
		||||
        pub AddrStack: ADDRESS64,
 | 
			
		||||
        pub AddrBStore: ADDRESS64,
 | 
			
		||||
        pub FuncTableEntry: PVOID,
 | 
			
		||||
        pub Params: [DWORD64; 4],
 | 
			
		||||
        pub Far: BOOL,
 | 
			
		||||
        pub Virtual: BOOL,
 | 
			
		||||
        pub Reserved: [DWORD64; 3],
 | 
			
		||||
        pub KdHelp: KDHELP64,
 | 
			
		||||
        pub StackFrameSize: DWORD,
 | 
			
		||||
        pub InlineFrameContext: DWORD,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type LPSTACKFRAME_EX = *mut STACKFRAME_EX;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct IMAGEHLP_LINEW64 {
 | 
			
		||||
        pub SizeOfStruct: DWORD,
 | 
			
		||||
        pub Key: PVOID,
 | 
			
		||||
        pub LineNumber: DWORD,
 | 
			
		||||
        pub FileName: PWSTR,
 | 
			
		||||
        pub Address: DWORD64,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type PIMAGEHLP_LINEW64 = *mut IMAGEHLP_LINEW64;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct SYMBOL_INFOW {
 | 
			
		||||
        pub SizeOfStruct: ULONG,
 | 
			
		||||
        pub TypeIndex: ULONG,
 | 
			
		||||
        pub Reserved: [ULONG64; 2],
 | 
			
		||||
        pub Index: ULONG,
 | 
			
		||||
        pub Size: ULONG,
 | 
			
		||||
        pub ModBase: ULONG64,
 | 
			
		||||
        pub Flags: ULONG,
 | 
			
		||||
        pub Value: ULONG64,
 | 
			
		||||
        pub Address: ULONG64,
 | 
			
		||||
        pub Register: ULONG,
 | 
			
		||||
        pub Scope: ULONG,
 | 
			
		||||
        pub Tag: ULONG,
 | 
			
		||||
        pub NameLen: ULONG,
 | 
			
		||||
        pub MaxNameLen: ULONG,
 | 
			
		||||
        pub Name: [WCHAR; 1],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type PSYMBOL_INFOW = *mut SYMBOL_INFOW;
 | 
			
		||||
 | 
			
		||||
    pub type PTRANSLATE_ADDRESS_ROUTINE64 = Option<
 | 
			
		||||
        unsafe extern "system" fn(hProcess: HANDLE, hThread: HANDLE, lpaddr: LPADDRESS64) -> DWORD64,
 | 
			
		||||
    >;
 | 
			
		||||
    pub type PGET_MODULE_BASE_ROUTINE64 =
 | 
			
		||||
        Option<unsafe extern "system" fn(hProcess: HANDLE, Address: DWORD64) -> DWORD64>;
 | 
			
		||||
    pub type PFUNCTION_TABLE_ACCESS_ROUTINE64 =
 | 
			
		||||
        Option<unsafe extern "system" fn(ahProcess: HANDLE, AddrBase: DWORD64) -> PVOID>;
 | 
			
		||||
    pub type PREAD_PROCESS_MEMORY_ROUTINE64 = Option<
 | 
			
		||||
        unsafe extern "system" fn(
 | 
			
		||||
            hProcess: HANDLE,
 | 
			
		||||
            qwBaseAddress: DWORD64,
 | 
			
		||||
            lpBuffer: PVOID,
 | 
			
		||||
            nSize: DWORD,
 | 
			
		||||
            lpNumberOfBytesRead: LPDWORD,
 | 
			
		||||
        ) -> BOOL,
 | 
			
		||||
    >;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct ADDRESS64 {
 | 
			
		||||
        pub Offset: DWORD64,
 | 
			
		||||
        pub Segment: WORD,
 | 
			
		||||
        pub Mode: ADDRESS_MODE,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub type LPADDRESS64 = *mut ADDRESS64;
 | 
			
		||||
 | 
			
		||||
    pub type ADDRESS_MODE = u32;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct KDHELP64 {
 | 
			
		||||
        pub Thread: DWORD64,
 | 
			
		||||
        pub ThCallbackStack: DWORD,
 | 
			
		||||
        pub ThCallbackBStore: DWORD,
 | 
			
		||||
        pub NextCallback: DWORD,
 | 
			
		||||
        pub FramePointer: DWORD,
 | 
			
		||||
        pub KiCallUserMode: DWORD64,
 | 
			
		||||
        pub KeUserCallbackDispatcher: DWORD64,
 | 
			
		||||
        pub SystemRangeStart: DWORD64,
 | 
			
		||||
        pub KiUserExceptionDispatcher: DWORD64,
 | 
			
		||||
        pub StackBase: DWORD64,
 | 
			
		||||
        pub StackLimit: DWORD64,
 | 
			
		||||
        pub BuildVersion: DWORD,
 | 
			
		||||
        pub Reserved0: DWORD,
 | 
			
		||||
        pub Reserved1: [DWORD64; 4],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct MODULEENTRY32W {
 | 
			
		||||
        pub dwSize: DWORD,
 | 
			
		||||
        pub th32ModuleID: DWORD,
 | 
			
		||||
        pub th32ProcessID: DWORD,
 | 
			
		||||
        pub GlblcntUsage: DWORD,
 | 
			
		||||
        pub ProccntUsage: DWORD,
 | 
			
		||||
        pub modBaseAddr: *mut u8,
 | 
			
		||||
        pub modBaseSize: DWORD,
 | 
			
		||||
        pub hModule: HMODULE,
 | 
			
		||||
        pub szModule: [WCHAR; MAX_MODULE_NAME32 + 1],
 | 
			
		||||
        pub szExePath: [WCHAR; MAX_PATH],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub const MAX_SYM_NAME: usize = 2000;
 | 
			
		||||
    pub const AddrModeFlat: ADDRESS_MODE = 3;
 | 
			
		||||
    pub const TRUE: BOOL = 1;
 | 
			
		||||
    pub const FALSE: BOOL = 0;
 | 
			
		||||
    pub const PROCESS_QUERY_INFORMATION: DWORD = 0x400;
 | 
			
		||||
    pub const IMAGE_FILE_MACHINE_ARM64: u16 = 43620;
 | 
			
		||||
    pub const IMAGE_FILE_MACHINE_AMD64: u16 = 34404;
 | 
			
		||||
    pub const IMAGE_FILE_MACHINE_I386: u16 = 332;
 | 
			
		||||
    pub const IMAGE_FILE_MACHINE_ARMNT: u16 = 452;
 | 
			
		||||
    pub const FILE_SHARE_READ: DWORD = 0x1;
 | 
			
		||||
    pub const FILE_SHARE_WRITE: DWORD = 0x2;
 | 
			
		||||
    pub const OPEN_EXISTING: DWORD = 0x3;
 | 
			
		||||
    pub const GENERIC_READ: DWORD = 0x80000000;
 | 
			
		||||
    pub const INFINITE: DWORD = !0;
 | 
			
		||||
    pub const PAGE_READONLY: DWORD = 2;
 | 
			
		||||
    pub const FILE_MAP_READ: DWORD = 4;
 | 
			
		||||
    pub const TH32CS_SNAPMODULE: DWORD = 0x00000008;
 | 
			
		||||
    pub const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
 | 
			
		||||
    pub const MAX_MODULE_NAME32: usize = 255;
 | 
			
		||||
    pub const MAX_PATH: usize = 260;
 | 
			
		||||
 | 
			
		||||
    pub type DWORD = u32;
 | 
			
		||||
    pub type PDWORD = *mut u32;
 | 
			
		||||
    pub type BOOL = i32;
 | 
			
		||||
    pub type DWORD64 = u64;
 | 
			
		||||
    pub type PDWORD64 = *mut u64;
 | 
			
		||||
    pub type HANDLE = *mut c_void;
 | 
			
		||||
    pub type PVOID = HANDLE;
 | 
			
		||||
    pub type PCWSTR = *const u16;
 | 
			
		||||
    pub type LPSTR = *mut i8;
 | 
			
		||||
    pub type LPCSTR = *const i8;
 | 
			
		||||
    pub type PWSTR = *mut u16;
 | 
			
		||||
    pub type WORD = u16;
 | 
			
		||||
    pub type ULONG = u32;
 | 
			
		||||
    pub type ULONG64 = u64;
 | 
			
		||||
    pub type WCHAR = u16;
 | 
			
		||||
    pub type PCONTEXT = *mut CONTEXT;
 | 
			
		||||
    pub type LPDWORD = *mut DWORD;
 | 
			
		||||
    pub type DWORDLONG = u64;
 | 
			
		||||
    pub type HMODULE = HINSTANCE;
 | 
			
		||||
    pub type SIZE_T = usize;
 | 
			
		||||
    pub type LPVOID = *mut c_void;
 | 
			
		||||
    pub type LPCVOID = *const c_void;
 | 
			
		||||
    pub type LPMODULEENTRY32W = *mut MODULEENTRY32W;
 | 
			
		||||
 | 
			
		||||
    #[link(name = "kernel32")]
 | 
			
		||||
    extern "system" {
 | 
			
		||||
        pub fn GetCurrentProcess() -> HANDLE;
 | 
			
		||||
        pub fn GetCurrentThread() -> HANDLE;
 | 
			
		||||
        pub fn RtlCaptureContext(ContextRecord: PCONTEXT) -> ();
 | 
			
		||||
        pub fn LoadLibraryA(a: *const i8) -> HMODULE;
 | 
			
		||||
        pub fn GetProcAddress(h: HMODULE, name: *const i8) -> FARPROC;
 | 
			
		||||
        pub fn GetModuleHandleA(name: *const i8) -> HMODULE;
 | 
			
		||||
        pub fn OpenProcess(
 | 
			
		||||
            dwDesiredAccess: DWORD,
 | 
			
		||||
            bInheitHandle: BOOL,
 | 
			
		||||
            dwProcessId: DWORD,
 | 
			
		||||
        ) -> HANDLE;
 | 
			
		||||
        pub fn GetCurrentProcessId() -> DWORD;
 | 
			
		||||
        pub fn CloseHandle(h: HANDLE) -> BOOL;
 | 
			
		||||
        pub fn CreateFileA(
 | 
			
		||||
            lpFileName: LPCSTR,
 | 
			
		||||
            dwDesiredAccess: DWORD,
 | 
			
		||||
            dwShareMode: DWORD,
 | 
			
		||||
            lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
 | 
			
		||||
            dwCreationDisposition: DWORD,
 | 
			
		||||
            dwFlagsAndAttributes: DWORD,
 | 
			
		||||
            hTemplateFile: HANDLE,
 | 
			
		||||
        ) -> HANDLE;
 | 
			
		||||
        pub fn CreateMutexA(
 | 
			
		||||
            attrs: LPSECURITY_ATTRIBUTES,
 | 
			
		||||
            initial: BOOL,
 | 
			
		||||
            name: LPCSTR,
 | 
			
		||||
        ) -> HANDLE;
 | 
			
		||||
        pub fn ReleaseMutex(hMutex: HANDLE) -> BOOL;
 | 
			
		||||
        pub fn WaitForSingleObjectEx(
 | 
			
		||||
            hHandle: HANDLE,
 | 
			
		||||
            dwMilliseconds: DWORD,
 | 
			
		||||
            bAlertable: BOOL,
 | 
			
		||||
        ) -> DWORD;
 | 
			
		||||
        pub fn CreateFileMappingA(
 | 
			
		||||
            hFile: HANDLE,
 | 
			
		||||
            lpFileMappingAttributes: LPSECURITY_ATTRIBUTES,
 | 
			
		||||
            flProtect: DWORD,
 | 
			
		||||
            dwMaximumSizeHigh: DWORD,
 | 
			
		||||
            dwMaximumSizeLow: DWORD,
 | 
			
		||||
            lpName: LPCSTR,
 | 
			
		||||
        ) -> HANDLE;
 | 
			
		||||
        pub fn MapViewOfFile(
 | 
			
		||||
            hFileMappingObject: HANDLE,
 | 
			
		||||
            dwDesiredAccess: DWORD,
 | 
			
		||||
            dwFileOffsetHigh: DWORD,
 | 
			
		||||
            dwFileOffsetLow: DWORD,
 | 
			
		||||
            dwNumberOfBytesToMap: SIZE_T,
 | 
			
		||||
        ) -> LPVOID;
 | 
			
		||||
        pub fn UnmapViewOfFile(lpBaseAddress: LPCVOID) -> BOOL;
 | 
			
		||||
        pub fn CreateToolhelp32Snapshot(
 | 
			
		||||
            dwFlags: DWORD,
 | 
			
		||||
            th32ProcessID: DWORD,
 | 
			
		||||
        ) -> HANDLE;
 | 
			
		||||
        pub fn Module32FirstW(
 | 
			
		||||
            hSnapshot: HANDLE,
 | 
			
		||||
            lpme: LPMODULEENTRY32W,
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
        pub fn Module32NextW(
 | 
			
		||||
            hSnapshot: HANDLE,
 | 
			
		||||
            lpme: LPMODULEENTRY32W,
 | 
			
		||||
        ) -> BOOL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_pointer_width = "64")]
 | 
			
		||||
ffi! {
 | 
			
		||||
    #[link(name = "kernel32")]
 | 
			
		||||
    extern "system" {
 | 
			
		||||
        pub fn RtlLookupFunctionEntry(
 | 
			
		||||
            ControlPc: DWORD64,
 | 
			
		||||
            ImageBase: PDWORD64,
 | 
			
		||||
            HistoryTable: PUNWIND_HISTORY_TABLE,
 | 
			
		||||
        ) -> PRUNTIME_FUNCTION;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "aarch64")]
 | 
			
		||||
ffi! {
 | 
			
		||||
    #[repr(C, align(16))]
 | 
			
		||||
    pub struct CONTEXT {
 | 
			
		||||
        pub ContextFlags: DWORD,
 | 
			
		||||
        pub Cpsr: DWORD,
 | 
			
		||||
        pub u: CONTEXT_u,
 | 
			
		||||
        pub Sp: u64,
 | 
			
		||||
        pub Pc: u64,
 | 
			
		||||
        pub V: [ARM64_NT_NEON128; 32],
 | 
			
		||||
        pub Fpcr: DWORD,
 | 
			
		||||
        pub Fpsr: DWORD,
 | 
			
		||||
        pub Bcr: [DWORD; ARM64_MAX_BREAKPOINTS],
 | 
			
		||||
        pub Bvr: [DWORD64; ARM64_MAX_BREAKPOINTS],
 | 
			
		||||
        pub Wcr: [DWORD; ARM64_MAX_WATCHPOINTS],
 | 
			
		||||
        pub Wvr: [DWORD64; ARM64_MAX_WATCHPOINTS],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct CONTEXT_u {
 | 
			
		||||
        pub s: CONTEXT_u_s,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl CONTEXT_u {
 | 
			
		||||
        pub unsafe fn s(&self) -> &CONTEXT_u_s {
 | 
			
		||||
            &self.s
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct CONTEXT_u_s {
 | 
			
		||||
        pub X0: u64,
 | 
			
		||||
        pub X1: u64,
 | 
			
		||||
        pub X2: u64,
 | 
			
		||||
        pub X3: u64,
 | 
			
		||||
        pub X4: u64,
 | 
			
		||||
        pub X5: u64,
 | 
			
		||||
        pub X6: u64,
 | 
			
		||||
        pub X7: u64,
 | 
			
		||||
        pub X8: u64,
 | 
			
		||||
        pub X9: u64,
 | 
			
		||||
        pub X10: u64,
 | 
			
		||||
        pub X11: u64,
 | 
			
		||||
        pub X12: u64,
 | 
			
		||||
        pub X13: u64,
 | 
			
		||||
        pub X14: u64,
 | 
			
		||||
        pub X15: u64,
 | 
			
		||||
        pub X16: u64,
 | 
			
		||||
        pub X17: u64,
 | 
			
		||||
        pub X18: u64,
 | 
			
		||||
        pub X19: u64,
 | 
			
		||||
        pub X20: u64,
 | 
			
		||||
        pub X21: u64,
 | 
			
		||||
        pub X22: u64,
 | 
			
		||||
        pub X23: u64,
 | 
			
		||||
        pub X24: u64,
 | 
			
		||||
        pub X25: u64,
 | 
			
		||||
        pub X26: u64,
 | 
			
		||||
        pub X27: u64,
 | 
			
		||||
        pub X28: u64,
 | 
			
		||||
        pub Fp: u64,
 | 
			
		||||
        pub Lr: u64,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub const ARM64_MAX_BREAKPOINTS: usize = 8;
 | 
			
		||||
    pub const ARM64_MAX_WATCHPOINTS: usize = 2;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct ARM64_NT_NEON128 {
 | 
			
		||||
        pub D: [f64; 2],
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "x86")]
 | 
			
		||||
ffi! {
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct CONTEXT {
 | 
			
		||||
        pub ContextFlags: DWORD,
 | 
			
		||||
        pub Dr0: DWORD,
 | 
			
		||||
        pub Dr1: DWORD,
 | 
			
		||||
        pub Dr2: DWORD,
 | 
			
		||||
        pub Dr3: DWORD,
 | 
			
		||||
        pub Dr6: DWORD,
 | 
			
		||||
        pub Dr7: DWORD,
 | 
			
		||||
        pub FloatSave: FLOATING_SAVE_AREA,
 | 
			
		||||
        pub SegGs: DWORD,
 | 
			
		||||
        pub SegFs: DWORD,
 | 
			
		||||
        pub SegEs: DWORD,
 | 
			
		||||
        pub SegDs: DWORD,
 | 
			
		||||
        pub Edi: DWORD,
 | 
			
		||||
        pub Esi: DWORD,
 | 
			
		||||
        pub Ebx: DWORD,
 | 
			
		||||
        pub Edx: DWORD,
 | 
			
		||||
        pub Ecx: DWORD,
 | 
			
		||||
        pub Eax: DWORD,
 | 
			
		||||
        pub Ebp: DWORD,
 | 
			
		||||
        pub Eip: DWORD,
 | 
			
		||||
        pub SegCs: DWORD,
 | 
			
		||||
        pub EFlags: DWORD,
 | 
			
		||||
        pub Esp: DWORD,
 | 
			
		||||
        pub SegSs: DWORD,
 | 
			
		||||
        pub ExtendedRegisters: [u8; 512],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct FLOATING_SAVE_AREA {
 | 
			
		||||
        pub ControlWord: DWORD,
 | 
			
		||||
        pub StatusWord: DWORD,
 | 
			
		||||
        pub TagWord: DWORD,
 | 
			
		||||
        pub ErrorOffset: DWORD,
 | 
			
		||||
        pub ErrorSelector: DWORD,
 | 
			
		||||
        pub DataOffset: DWORD,
 | 
			
		||||
        pub DataSelector: DWORD,
 | 
			
		||||
        pub RegisterArea: [u8; 80],
 | 
			
		||||
        pub Spare0: DWORD,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "x86_64")]
 | 
			
		||||
ffi! {
 | 
			
		||||
    #[repr(C, align(8))]
 | 
			
		||||
    pub struct CONTEXT {
 | 
			
		||||
        pub P1Home: DWORDLONG,
 | 
			
		||||
        pub P2Home: DWORDLONG,
 | 
			
		||||
        pub P3Home: DWORDLONG,
 | 
			
		||||
        pub P4Home: DWORDLONG,
 | 
			
		||||
        pub P5Home: DWORDLONG,
 | 
			
		||||
        pub P6Home: DWORDLONG,
 | 
			
		||||
 | 
			
		||||
        pub ContextFlags: DWORD,
 | 
			
		||||
        pub MxCsr: DWORD,
 | 
			
		||||
 | 
			
		||||
        pub SegCs: WORD,
 | 
			
		||||
        pub SegDs: WORD,
 | 
			
		||||
        pub SegEs: WORD,
 | 
			
		||||
        pub SegFs: WORD,
 | 
			
		||||
        pub SegGs: WORD,
 | 
			
		||||
        pub SegSs: WORD,
 | 
			
		||||
        pub EFlags: DWORD,
 | 
			
		||||
 | 
			
		||||
        pub Dr0: DWORDLONG,
 | 
			
		||||
        pub Dr1: DWORDLONG,
 | 
			
		||||
        pub Dr2: DWORDLONG,
 | 
			
		||||
        pub Dr3: DWORDLONG,
 | 
			
		||||
        pub Dr6: DWORDLONG,
 | 
			
		||||
        pub Dr7: DWORDLONG,
 | 
			
		||||
 | 
			
		||||
        pub Rax: DWORDLONG,
 | 
			
		||||
        pub Rcx: DWORDLONG,
 | 
			
		||||
        pub Rdx: DWORDLONG,
 | 
			
		||||
        pub Rbx: DWORDLONG,
 | 
			
		||||
        pub Rsp: DWORDLONG,
 | 
			
		||||
        pub Rbp: DWORDLONG,
 | 
			
		||||
        pub Rsi: DWORDLONG,
 | 
			
		||||
        pub Rdi: DWORDLONG,
 | 
			
		||||
        pub R8:  DWORDLONG,
 | 
			
		||||
        pub R9:  DWORDLONG,
 | 
			
		||||
        pub R10: DWORDLONG,
 | 
			
		||||
        pub R11: DWORDLONG,
 | 
			
		||||
        pub R12: DWORDLONG,
 | 
			
		||||
        pub R13: DWORDLONG,
 | 
			
		||||
        pub R14: DWORDLONG,
 | 
			
		||||
        pub R15: DWORDLONG,
 | 
			
		||||
 | 
			
		||||
        pub Rip: DWORDLONG,
 | 
			
		||||
 | 
			
		||||
        pub FltSave: FLOATING_SAVE_AREA,
 | 
			
		||||
 | 
			
		||||
        pub VectorRegister: [M128A; 26],
 | 
			
		||||
        pub VectorControl: DWORDLONG,
 | 
			
		||||
 | 
			
		||||
        pub DebugControl: DWORDLONG,
 | 
			
		||||
        pub LastBranchToRip: DWORDLONG,
 | 
			
		||||
        pub LastBranchFromRip: DWORDLONG,
 | 
			
		||||
        pub LastExceptionToRip: DWORDLONG,
 | 
			
		||||
        pub LastExceptionFromRip: DWORDLONG,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct M128A {
 | 
			
		||||
        pub Low: u64,
 | 
			
		||||
        pub High: i64,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[cfg(target_arch = "x86_64")]
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
pub struct FLOATING_SAVE_AREA {
 | 
			
		||||
    _Dummy: [u8; 512],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(target_arch = "arm")]
 | 
			
		||||
ffi! {
 | 
			
		||||
    // #[repr(C)]
 | 
			
		||||
    // pub struct NEON128 {
 | 
			
		||||
    //     pub Low: ULONG64,
 | 
			
		||||
    //     pub High: LONG64,
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    // pub type PNEON128 = *mut NEON128;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct CONTEXT_u {
 | 
			
		||||
        // pub Q: [NEON128; 16],
 | 
			
		||||
        pub D: [ULONG64; 32],
 | 
			
		||||
        // pub S: [DWORD; 32],
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub const ARM_MAX_BREAKPOINTS: usize = 8;
 | 
			
		||||
    pub const ARM_MAX_WATCHPOINTS: usize = 1;
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct CONTEXT {
 | 
			
		||||
        pub ContextFlags: DWORD,
 | 
			
		||||
        pub R0: DWORD,
 | 
			
		||||
        pub R1: DWORD,
 | 
			
		||||
        pub R2: DWORD,
 | 
			
		||||
        pub R3: DWORD,
 | 
			
		||||
        pub R4: DWORD,
 | 
			
		||||
        pub R5: DWORD,
 | 
			
		||||
        pub R6: DWORD,
 | 
			
		||||
        pub R7: DWORD,
 | 
			
		||||
        pub R8: DWORD,
 | 
			
		||||
        pub R9: DWORD,
 | 
			
		||||
        pub R10: DWORD,
 | 
			
		||||
        pub R11: DWORD,
 | 
			
		||||
        pub R12: DWORD,
 | 
			
		||||
        pub Sp: DWORD,
 | 
			
		||||
        pub Lr: DWORD,
 | 
			
		||||
        pub Pc: DWORD,
 | 
			
		||||
        pub Cpsr: DWORD,
 | 
			
		||||
        pub Fpsrc: DWORD,
 | 
			
		||||
        pub Padding: DWORD,
 | 
			
		||||
        pub u: CONTEXT_u,
 | 
			
		||||
        pub Bvr: [DWORD; ARM_MAX_BREAKPOINTS],
 | 
			
		||||
        pub Bcr: [DWORD; ARM_MAX_BREAKPOINTS],
 | 
			
		||||
        pub Wvr: [DWORD; ARM_MAX_WATCHPOINTS],
 | 
			
		||||
        pub Wcr: [DWORD; ARM_MAX_WATCHPOINTS],
 | 
			
		||||
        pub Padding2: [DWORD; 2],
 | 
			
		||||
    }
 | 
			
		||||
} // IFDEF(arm)
 | 
			
		||||
		Reference in New Issue
	
	Block a user