blob: 865f691810f18637faf41f790fa76b63c3a890ce [file] [log] [blame]
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style)]
extern crate backtrace_sys as bt;
use libc::uintptr_t;
use std::ffi::{CStr, OsStr};
use std::os::raw::{c_void, c_char, c_int};
use std::os::unix::prelude::*;
use std::path::Path;
use std::ptr;
use std::sync::{ONCE_INIT, Once};
use SymbolName;
pub enum Symbol {
Syminfo {
pc: uintptr_t,
symname: *const c_char,
},
Pcinfo {
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char,
},
}
impl Symbol {
pub fn name(&self) -> Option<SymbolName> {
let ptr = match *self {
Symbol::Syminfo { symname, .. } => symname,
Symbol::Pcinfo { function, .. } => function,
};
if ptr.is_null() {
None
} else {
Some(SymbolName::new(unsafe { CStr::from_ptr(ptr).to_bytes() }))
}
}
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 _)}
}
pub fn filename(&self) -> Option<&Path> {
match *self {
Symbol::Syminfo { .. } => None,
Symbol::Pcinfo { filename, .. } => {
Some(Path::new(OsStr::from_bytes(unsafe {
CStr::from_ptr(filename).to_bytes()
})))
}
}
}
pub fn lineno(&self) -> Option<u32> {
match *self {
Symbol::Syminfo { .. } => None,
Symbol::Pcinfo { lineno, .. } => Some(lineno as u32),
}
}
}
extern fn error_cb(_data: *mut c_void, _msg: *const c_char,
_errnum: c_int) {
// do nothing for now
}
extern fn syminfo_cb(data: *mut c_void,
pc: uintptr_t,
symname: *const c_char,
_symval: uintptr_t,
_symsize: uintptr_t) {
unsafe {
call(data, &super::Symbol {
inner: Symbol::Syminfo {
pc: pc,
symname: symname,
},
});
}
}
extern fn pcinfo_cb(data: *mut c_void,
pc: uintptr_t,
filename: *const c_char,
lineno: c_int,
function: *const c_char) -> c_int {
unsafe {
if filename.is_null() || function.is_null() {
return -1
}
call(data, &super::Symbol {
inner: Symbol::Pcinfo {
pc: pc,
filename: filename,
lineno: lineno,
function: function,
},
});
return 0
}
}
unsafe fn call(data: *mut c_void, sym: &super::Symbol) {
let cb = data as *mut &mut FnMut(&super::Symbol);
let mut bomb = ::Bomb { enabled: true };
(*cb)(sym);
bomb.enabled = false;
}
// The libbacktrace API supports creating a state, but it does not
// support destroying a state. I personally take this to mean that a
// state is meant to be created and then live forever.
//
// I would love to register an at_exit() handler which cleans up this
// state, but libbacktrace provides no way to do so.
//
// With these constraints, this function has a statically cached state
// that is calculated the first time this is requested. Remember that
// backtracing all happens serially (one global lock).
//
// Things don't work so well on not-Linux since libbacktrace can't track down
// that executable this is. We at one point used env::current_exe but it turns
// out that there are some serious security issues with that approach.
//
// Specifically, on certain platforms like BSDs, a malicious actor can cause an
// arbitrary file to be placed at the path returned by current_exe. libbacktrace
// does not behave defensively in the presence of ill-formed DWARF information,
// and has been demonstrated to segfault in at least one case. There is no
// evidence at the moment to suggest that a more carefully constructed file
// can't cause arbitrary code execution. As a result of all of this, we don't
// hint libbacktrace with the path to the current process.
unsafe fn init_state() -> *mut bt::backtrace_state {
static mut STATE: *mut bt::backtrace_state = 0 as *mut _;
static INIT: Once = ONCE_INIT;
INIT.call_once(|| {
// Our libbacktrace may not have multithreading support, so
// set `threaded = 0` and synchronize ourselves.
STATE = bt::backtrace_create_state(ptr::null(), 0, error_cb,
ptr::null_mut());
});
STATE
}
pub fn resolve(symaddr: *mut c_void, mut cb: &mut FnMut(&super::Symbol)) {
let _guard = ::lock::lock();
// backtrace errors are currently swept under the rug
unsafe {
let state = init_state();
if state.is_null() {
return
}
let ret = bt::backtrace_pcinfo(state, symaddr as uintptr_t,
pcinfo_cb, error_cb,
&mut cb as *mut _ as *mut _);
if ret != 0 {
bt::backtrace_syminfo(state, symaddr as uintptr_t,
syminfo_cb, error_cb,
&mut cb as *mut _ as *mut _);
}
}
}