blob: 24dc11a8e1c74a995c5aeda2590cfb3db2fd6341 [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.
//! Symbolication strategy that's OSX-specific and uses the `CoreSymbolication`
//! framework, if possible.
//!
//! This strategy uses internal private APIs that are somewhat undocumented but
//! seem to be widely used on OSX. This is the default symbolication strategy
//! for OSX, but is turned off in release builds for iOS due to reports of apps
//! being rejected due to using these APIs.
//!
//! This would probably be good to get official one day and not using private
//! APIs, but for now it should largely suffice.
//!
//! Note that this module will dynamically load `CoreSymbolication` and its APIs
//! through dlopen/dlsym, and if the loading fails this falls back to `dladdr`
//! as a symbolication strategy.
#![allow(bad_style)]
use crate::symbolize::dladdr;
use crate::symbolize::ResolveWhat;
use crate::types::BytesOrWideString;
use crate::SymbolName;
use core::ffi::c_void;
use core::mem;
use core::ptr;
use core::slice;
use libc::{c_char, c_int};
#[repr(C)]
#[derive(Copy, Clone, PartialEq)]
pub struct CSTypeRef {
cpp_data: *const c_void,
cpp_obj: *const c_void,
}
const CS_NOW: u64 = 0x80000000;
const CSREF_NULL: CSTypeRef = CSTypeRef {
cpp_data: 0 as *const c_void,
cpp_obj: 0 as *const c_void,
};
pub enum Symbol<'a> {
Core {
path: *const c_char,
lineno: u32,
name: *const c_char,
addr: *mut c_void,
},
Dladdr(dladdr::Symbol<'a>),
}
impl Symbol<'_> {
pub fn name(&self) -> Option<SymbolName> {
let name = match *self {
Symbol::Core { name, .. } => name,
Symbol::Dladdr(ref info) => return info.name(),
};
if name.is_null() {
None
} else {
Some(SymbolName::new(unsafe {
let len = libc::strlen(name);
slice::from_raw_parts(name as *const u8, len)
}))
}
}
pub fn addr(&self) -> Option<*mut c_void> {
match *self {
Symbol::Core { addr, .. } => Some(addr),
Symbol::Dladdr(ref info) => info.addr(),
}
}
fn filename_bytes(&self) -> Option<&[u8]> {
match *self {
Symbol::Core { path, .. } => {
if path.is_null() {
None
} else {
Some(unsafe {
let len = libc::strlen(path);
slice::from_raw_parts(path as *const u8, len)
})
}
}
Symbol::Dladdr(_) => None,
}
}
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::ffi::OsStr;
use std::os::unix::prelude::*;
use std::path::Path;
self.filename_bytes().map(OsStr::from_bytes).map(Path::new)
}
pub fn lineno(&self) -> Option<u32> {
match *self {
Symbol::Core { lineno: 0, .. } => None,
Symbol::Core { lineno, .. } => Some(lineno),
Symbol::Dladdr(_) => None,
}
}
}
macro_rules! coresymbolication {
(#[load_path = $path:tt] extern "C" {
$(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)*
}) => (
pub struct CoreSymbolication {
// The loaded dynamic library
dll: *mut c_void,
// Each function pointer for each function we might use
$($name: usize,)*
}
static mut CORESYMBOLICATION: CoreSymbolication = CoreSymbolication {
// Initially we haven't loaded the dynamic library
dll: 0 as *mut _,
// Initiall 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 "C" fn($($argty),*) -> $ret;)*
impl CoreSymbolication {
/// Attempts to open `dbghelp.dll`. Returns `true` if it works or
/// `false` if `dlopen` fails.
fn open(&mut self) -> bool {
if !self.dll.is_null() {
return true;
}
let lib = concat!($path, "\0").as_bytes();
unsafe {
self.dll = libc::dlopen(lib.as_ptr() as *const _, libc::RTLD_LAZY);
!self.dll.is_null()
}
}
// 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) -> $name {
unsafe {
if self.$name == 0 {
let name = concat!(stringify!($name), "\0");
self.$name = self.symbol(name.as_bytes())
.expect(concat!("symbol ", stringify!($name), " is missing"));
}
mem::transmute::<usize, $name>(self.$name)
}
})*
fn symbol(&self, symbol: &[u8]) -> Option<usize> {
unsafe {
match libc::dlsym(self.dll, symbol.as_ptr() as *const _) as usize {
0 => None,
n => Some(n),
}
}
}
}
)
}
coresymbolication! {
#[load_path = "/System/Library/PrivateFrameworks/CoreSymbolication.framework\
/Versions/A/CoreSymbolication"]
extern "C" {
fn CSSymbolicatorCreateWithPid(pid: c_int) -> CSTypeRef;
fn CSRelease(rf: CSTypeRef) -> ();
fn CSSymbolicatorGetSymbolWithAddressAtTime(
cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef;
fn CSSymbolicatorGetSourceInfoWithAddressAtTime(
cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef;
fn CSSourceInfoGetLineNumber(info: CSTypeRef) -> c_int;
fn CSSourceInfoGetPath(info: CSTypeRef) -> *const c_char;
fn CSSourceInfoGetSymbol(info: CSTypeRef) -> CSTypeRef;
fn CSSymbolGetMangledName(sym: CSTypeRef) -> *const c_char;
fn CSSymbolGetSymbolOwner(sym: CSTypeRef) -> CSTypeRef;
fn CSSymbolOwnerGetBaseAddress(symowner: CSTypeRef) -> *mut c_void;
}
}
unsafe fn try_resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) -> bool {
// Note that this is externally synchronized so there's no need for
// synchronization here, making this `static mut` safer.
let lib = &mut CORESYMBOLICATION;
if !lib.open() {
return false;
}
let cs = lib.CSSymbolicatorCreateWithPid()(libc::getpid());
if cs == CSREF_NULL {
return false;
}
let _dtor = OwnedCSTypeRef {
ptr: cs,
CSRelease: lib.CSRelease(),
};
let info = lib.CSSymbolicatorGetSourceInfoWithAddressAtTime()(cs, addr, CS_NOW);
let sym = if info == CSREF_NULL {
lib.CSSymbolicatorGetSymbolWithAddressAtTime()(cs, addr, CS_NOW)
} else {
lib.CSSourceInfoGetSymbol()(info)
};
if sym == CSREF_NULL {
return false;
}
let owner = lib.CSSymbolGetSymbolOwner()(sym);
if owner == CSREF_NULL {
return false;
}
cb(&super::Symbol {
inner: Symbol::Core {
path: if info != CSREF_NULL {
lib.CSSourceInfoGetPath()(info)
} else {
ptr::null()
},
lineno: if info != CSREF_NULL {
lib.CSSourceInfoGetLineNumber()(info) as u32
} else {
0
},
name: lib.CSSymbolGetMangledName()(sym),
addr: lib.CSSymbolOwnerGetBaseAddress()(owner),
},
});
true
}
struct OwnedCSTypeRef {
ptr: CSTypeRef,
CSRelease: unsafe extern "C" fn(CSTypeRef),
}
impl Drop for OwnedCSTypeRef {
fn drop(&mut self) {
unsafe {
(self.CSRelease)(self.ptr);
}
}
}
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
let addr = what.address_or_ip();
if try_resolve(addr, cb) {
return;
}
dladdr::resolve(addr, &mut |sym| {
cb(&super::Symbol {
inner: Symbol::Dladdr(sym),
})
})
}