#![allow(bad_style)]
extern crate backtrace_sys as bt;
use core::{marker, ptr, slice};
use libc::{self, c_char, c_int, c_void, uintptr_t};
use crate::symbolize::{ResolveWhat, SymbolName};
use crate::types::BytesOrWideString;
pub enum Symbol<'a> {
Syminfo {
pc: uintptr_t,
symname: *const c_char,
_marker: marker::PhantomData<&'a ()>,
},
Pcinfo {
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
symname: *const c_char,
},
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName<'_>> {
let symbol = |ptr: *const c_char| unsafe {
if ptr.is_null() {
None
} else {
let len = libc::strlen(ptr);
Some(SymbolName::new(slice::from_raw_parts(
ptr as *const u8,
len,
)))
}
};
match *self {
Symbol::Syminfo { symname, .. } => symbol(symname),
Symbol::Pcinfo {
function, symname, ..
} => {
if let Some(sym) = symbol(function) {
return Some(sym);
}
symbol(symname)
}
}
}
pub fn addr(&self) -> Option<*mut c_void> {
let pc = match *self {
Symbol::Syminfo { pc, .. } => pc,
Symbol::Pcinfo { pc, .. } => pc,
};
if pc == 0 {
None
} else {
Some(pc as *mut _)
}
}
fn filename_bytes(&self) -> Option<&[u8]> {
match *self {
Symbol::Syminfo { .. } => None,
Symbol::Pcinfo { filename, .. } => {
let ptr = filename as *const u8;
if ptr.is_null() {
return None;
}
unsafe {
let len = libc::strlen(filename);
Some(slice::from_raw_parts(ptr, len))
}
}
}
}
pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
self.filename_bytes().map(BytesOrWideString::Bytes)
}
#[cfg(feature = "std")]
pub fn filename(&self) -> Option<&::std::path::Path> {
use std::path::Path;
#[cfg(unix)]
fn bytes2path(bytes: &[u8]) -> Option<&Path> {
use std::ffi::OsStr;
use std::os::unix::prelude::*;
Some(Path::new(OsStr::from_bytes(bytes)))
}
#[cfg(windows)]
fn bytes2path(bytes: &[u8]) -> Option<&Path> {
use std::str;
str::from_utf8(bytes).ok().map(Path::new)
}
self.filename_bytes().and_then(bytes2path)
}
pub fn lineno(&self) -> Option<u32> {
match *self {
Symbol::Syminfo { .. } => None,
Symbol::Pcinfo { lineno, .. } => Some(lineno as u32),
}
}
}
extern "C" fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int) {
}
struct SyminfoState<'a> {
cb: &'a mut (dyn FnMut(&super::Symbol) + 'a),
pc: usize,
}
extern "C" fn syminfo_cb(
data: *mut c_void,
pc: uintptr_t,
symname: *const c_char,
_symval: uintptr_t,
_symsize: uintptr_t,
) {
let mut bomb = crate::Bomb { enabled: true };
unsafe {
let syminfo_state = &mut *(data as *mut SyminfoState<'_>);
let mut pcinfo_state = PcinfoState {
symname,
called: false,
cb: syminfo_state.cb,
};
bt::backtrace_pcinfo(
init_state(),
syminfo_state.pc as uintptr_t,
pcinfo_cb,
error_cb,
&mut pcinfo_state as *mut _ as *mut _,
);
if !pcinfo_state.called {
(pcinfo_state.cb)(&super::Symbol {
inner: Symbol::Syminfo {
pc: pc,
symname: symname,
_marker: marker::PhantomData,
},
});
}
}
bomb.enabled = false;
}
struct PcinfoState<'a> {
cb: &'a mut (dyn FnMut(&super::Symbol) + 'a),
symname: *const c_char,
called: bool,
}
extern "C" fn pcinfo_cb(
data: *mut c_void,
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
) -> c_int {
let mut bomb = crate::Bomb { enabled: true };
unsafe {
let state = &mut *(data as *mut PcinfoState<'_>);
state.called = true;
(state.cb)(&super::Symbol {
inner: Symbol::Pcinfo {
pc: pc,
filename: filename,
lineno: lineno,
symname: state.symname,
function,
},
});
}
bomb.enabled = false;
return 0;
}
unsafe fn init_state() -> *mut bt::backtrace_state {
static mut STATE: *mut bt::backtrace_state = 0 as *mut _;
if !STATE.is_null() {
return STATE;
}
STATE = bt::backtrace_create_state(
load_filename(),
0,
error_cb,
ptr::null_mut(), );
return STATE;
cfg_if::cfg_if! {
if #[cfg(any(target_os = "macos", target_os = "ios"))] {
unsafe fn load_filename() -> *const libc::c_char {
const N: usize = 256;
static mut BUF: [u8; N] = [0; N];
extern {
fn _NSGetExecutablePath(
buf: *mut libc::c_char,
bufsize: *mut u32,
) -> libc::c_int;
}
let mut sz: u32 = BUF.len() as u32;
let ptr = BUF.as_mut_ptr() as *mut libc::c_char;
if _NSGetExecutablePath(ptr, &mut sz) == 0 {
ptr
} else {
ptr::null()
}
}
} else if #[cfg(windows)] {
use crate::windows::*;
unsafe fn load_filename() -> *const libc::c_char {
load_filename_opt().unwrap_or(ptr::null())
}
unsafe fn load_filename_opt() -> Result<*const libc::c_char, ()> {
const N: usize = 256;
static mut BUF: [i8; N] = [0; N];
let mut stack_buf = [0; N];
let name1 = query_full_name(&mut BUF)?;
let handle = CreateFileA(
name1.as_ptr(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
ptr::null_mut(),
OPEN_EXISTING,
0,
ptr::null_mut(),
);
if handle.is_null() {
return Err(());
}
let name2 = query_full_name(&mut stack_buf)?;
if name1 != name2 {
CloseHandle(handle);
return Err(())
}
Ok(name1.as_ptr())
}
unsafe fn query_full_name(buf: &mut [i8]) -> Result<&[i8], ()> {
let dll = GetModuleHandleA(b"kernel32.dll\0".as_ptr() as *const i8);
if dll.is_null() {
return Err(())
}
let ptrQueryFullProcessImageNameA =
GetProcAddress(dll, b"QueryFullProcessImageNameA\0".as_ptr() as *const _) as usize;
if ptrQueryFullProcessImageNameA == 0
{
return Err(());
}
use core::mem;
let p1 = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
let mut len = buf.len() as u32;
let pfnQueryFullProcessImageNameA : extern "system" fn(
hProcess: HANDLE,
dwFlags: DWORD,
lpExeName: LPSTR,
lpdwSize: PDWORD,
) -> BOOL = mem::transmute(ptrQueryFullProcessImageNameA);
let rc = pfnQueryFullProcessImageNameA(p1, 0, buf.as_mut_ptr(), &mut len);
CloseHandle(p1);
if rc == 0 || len == buf.len() as u32 {
Err(())
} else {
assert_eq!(buf[len as usize], 0);
Ok(&buf[..(len + 1) as usize])
}
}
} else if #[cfg(target_os = "vxworks")] {
unsafe fn load_filename() -> *const libc::c_char {
use libc;
use core::mem;
const N: usize = libc::VX_RTP_NAME_LENGTH as usize + 1;
static mut BUF: [libc::c_char; N] = [0; N];
let mut rtp_desc : libc::RTP_DESC = mem::zeroed();
if (libc::rtpInfoGet(0, &mut rtp_desc as *mut libc::RTP_DESC) == 0) {
BUF.copy_from_slice(&rtp_desc.pathName);
BUF.as_ptr()
} else {
ptr::null()
}
}
} else {
unsafe fn load_filename() -> *const libc::c_char {
ptr::null()
}
}
}
}
pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
let symaddr = what.address_or_ip() as usize;
let state = init_state();
if state.is_null() {
return;
}
let mut syminfo_state = SyminfoState { pc: symaddr, cb };
bt::backtrace_syminfo(
state,
symaddr as uintptr_t,
syminfo_cb,
error_cb,
&mut syminfo_state as *mut _ as *mut _,
);
}