use libc::{c_char, c_int, c_void, pid_t, size_t};
use std;
#[cfg_attr(target_arch = "x86_64", path = "bindings_x86_64.rs")]
#[cfg_attr(target_arch = "arm", path = "bindings_arm.rs")]
#[cfg_attr(target_arch = "aarch64", path = "bindings_aarch64.rs")]
mod bindings;
use self::bindings::{
unw_accessors_t, unw_addr_space_t, unw_caching_policy_t,
unw_caching_policy_t_UNW_CACHE_PER_THREAD, unw_cursor, unw_cursor_t,
unw_frame_regnum_t_UNW_REG_IP, unw_frame_regnum_t_UNW_REG_SP, unw_regnum_t, unw_word_t,
};
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum Error {
UNW_EUNSPEC,
UNW_ENOMEM,
UNW_EBADREG,
UNW_EREADONLYREG,
UNW_ESTOPUNWIND,
UNW_EINVALIDIP,
UNW_EBADFRAME,
UNW_EINVAL,
UNW_EBADVERSION,
UNW_ENOINFO,
}
type Result<T> = std::result::Result<T, crate::Error>;
pub struct Unwinder {
pub addr_space: unw_addr_space_t,
}
impl Unwinder {
pub fn new() -> Result<Unwinder> {
unsafe {
let addr_space = create_addr_space(&_UPT_accessors as *const _ as *mut _, 0);
set_caching_policy(addr_space, unw_caching_policy_t_UNW_CACHE_PER_THREAD);
Ok(Unwinder { addr_space })
}
}
pub fn cursor(&self, thread: &crate::Thread) -> Result<Cursor> {
unsafe {
let upt = _UPT_create(thread.id()? as _);
let mut cursor = std::mem::MaybeUninit::uninit();
let ret = init_remote(cursor.as_mut_ptr(), self.addr_space, upt);
if ret != 0 {
return Err(crate::Error::LibunwindError(Error::from(-ret)));
}
Ok(Cursor {
cursor: cursor.assume_init(),
upt,
initial_frame: true,
})
}
}
}
impl Drop for Unwinder {
fn drop(&mut self) {
unsafe {
destroy_addr_space(self.addr_space);
}
}
}
pub struct Cursor {
cursor: unw_cursor,
upt: *mut c_void,
initial_frame: bool,
}
impl Cursor {
pub unsafe fn register(&self, register: i32) -> Result<u64> {
let mut value = 0;
let cursor = &self.cursor as *const _ as *mut _;
match get_reg(cursor, register, &mut value) {
0 => Ok(value as u64),
err => Err(crate::Error::LibunwindError(Error::from(-err))),
}
}
#[cfg(target_arch = "x86_64")]
pub fn bx(&self) -> Result<u64> {
unsafe { self.register(3) }
}
#[cfg(target_arch = "arm")]
pub fn r5(&self) -> Result<u64> {
unsafe { self.register(5) }
}
#[cfg(target_arch = "aarch64")]
pub fn r5(&self) -> Result<u64> {
unsafe { self.register(5) }
}
pub fn ip(&self) -> Result<u64> {
unsafe { self.register(unw_frame_regnum_t_UNW_REG_IP as i32) }
}
pub fn sp(&self) -> Result<u64> {
unsafe { self.register(unw_frame_regnum_t_UNW_REG_SP as i32) }
}
pub fn proc_name(&self) -> Result<String> {
unsafe {
let mut name = vec![0_u8 as c_char; 128];
let cursor = &self.cursor as *const _ as *mut _;
let mut raw_offset = 0;
loop {
match get_proc_name(cursor, name.as_mut_ptr(), name.len(), &mut raw_offset) {
0 => break,
-2 => {
let new_length = name.len() * 2;
name.resize(new_length, 0);
continue;
}
err => {
return Err(crate::Error::LibunwindError(Error::from(-err)));
}
}
}
Ok(std::ffi::CStr::from_ptr(name.as_ptr())
.to_string_lossy()
.into_owned())
}
}
}
impl Iterator for Cursor {
type Item = Result<u64>;
fn next(&mut self) -> Option<Result<u64>> {
if !self.initial_frame {
unsafe {
match step(&mut self.cursor) {
0 => return None,
err if err < 0 => {
return Some(Err(crate::Error::LibunwindError(Error::from(-err))))
}
_ => {}
}
};
} else {
self.initial_frame = false;
}
match self.ip() {
Ok(0) => None,
Ok(ip) => Some(Ok(ip)),
Err(e) => Some(Err(e)),
}
}
}
impl Drop for Cursor {
fn drop(&mut self) {
unsafe {
_UPT_destroy(self.upt);
}
}
}
extern "C" {
fn _UPT_create(pid: pid_t) -> *mut c_void;
fn _UPT_destroy(p: *mut c_void) -> c_void;
#[allow(improper_ctypes)]
static _UPT_accessors: unw_accessors_t;
}
#[cfg(target_arch = "x86_64")]
extern "C" {
#[link_name = "_Ux86_64_create_addr_space"]
#[allow(improper_ctypes)]
fn create_addr_space(acc: *mut unw_accessors_t, byteorder: c_int) -> unw_addr_space_t;
#[link_name = "_Ux86_64_destroy_addr_space"]
fn destroy_addr_space(addr: unw_addr_space_t) -> c_void;
#[link_name = "_Ux86_64_init_remote"]
fn init_remote(cursor: *mut unw_cursor_t, addr: unw_addr_space_t, ptr: *mut c_void) -> c_int;
#[link_name = "_Ux86_64_get_reg"]
fn get_reg(cursor: *mut unw_cursor_t, reg: unw_regnum_t, val: *mut unw_word_t) -> c_int;
#[link_name = "_Ux86_64_step"]
fn step(cursor: *mut unw_cursor_t) -> c_int;
#[link_name = "_Ux86_64_get_proc_name"]
fn get_proc_name(
cursor: *mut unw_cursor,
buffer: *mut c_char,
len: size_t,
offset: *mut unw_word_t,
) -> c_int;
#[link_name = "_Ux86_64_set_caching_policy"]
fn set_caching_policy(spc: unw_addr_space_t, policy: unw_caching_policy_t) -> c_int;
}
#[cfg(target_arch = "x86")]
extern "C" {
#[link_name = "_Ux86_create_addr_space"]
fn create_addr_space(acc: *mut unw_accessors_t, byteorder: c_int) -> unw_addr_space_t;
#[link_name = "_Ux86_destroy_addr_space"]
fn destroy_addr_space(addr: unw_addr_space_t) -> c_void;
#[link_name = "_Ux86_init_remote"]
fn init_remote(cursor: *mut unw_cursor_t, addr: unw_addr_space_t, ptr: *mut c_void) -> c_int;
#[link_name = "_Ux86_get_reg"]
fn get_reg(cursor: *mut unw_cursor_t, reg: unw_regnum_t, val: *mut unw_word_t) -> c_int;
#[link_name = "_Ux86_step"]
fn step(cursor: *mut unw_cursor_t) -> c_int;
#[link_name = "_Ux86_get_proc_name"]
fn get_proc_name(
cursor: *mut unw_cursor,
buffer: *mut c_char,
len: size_t,
offset: *mut unw_word_t,
) -> c_int;
#[link_name = "_Ux86_set_caching_policy"]
fn set_caching_policy(spc: unw_addr_space_t, policy: unw_caching_policy_t) -> c_int;
}
#[cfg(target_arch = "arm")]
extern "C" {
#[link_name = "_Uarm_create_addr_space"]
#[allow(improper_ctypes)]
fn create_addr_space(acc: *mut unw_accessors_t, byteorder: c_int) -> unw_addr_space_t;
#[link_name = "_Uarm_destroy_addr_space"]
fn destroy_addr_space(addr: unw_addr_space_t) -> c_void;
#[link_name = "_Uarm_init_remote"]
fn init_remote(cursor: *mut unw_cursor_t, addr: unw_addr_space_t, ptr: *mut c_void) -> c_int;
#[link_name = "_Uarm_get_reg"]
fn get_reg(cursor: *mut unw_cursor_t, reg: unw_regnum_t, val: *mut unw_word_t) -> c_int;
#[link_name = "_Uarm_step"]
fn step(cursor: *mut unw_cursor_t) -> c_int;
#[link_name = "_Uarm_get_proc_name"]
fn get_proc_name(
cursor: *mut unw_cursor,
buffer: *mut c_char,
len: size_t,
offset: *mut unw_word_t,
) -> c_int;
#[link_name = "_Uarm_set_caching_policy"]
fn set_caching_policy(spc: unw_addr_space_t, policy: unw_caching_policy_t) -> c_int;
}
#[cfg(target_arch = "aarch64")]
extern "C" {
#[link_name = "_Uaarch64_create_addr_space"]
#[allow(improper_ctypes)]
fn create_addr_space(acc: *mut unw_accessors_t, byteorder: c_int) -> unw_addr_space_t;
#[link_name = "_Uaarch64_destroy_addr_space"]
fn destroy_addr_space(addr: unw_addr_space_t) -> c_void;
#[link_name = "_Uaarch64_init_remote"]
fn init_remote(cursor: *mut unw_cursor_t, addr: unw_addr_space_t, ptr: *mut c_void) -> c_int;
#[link_name = "_Uaarch64_get_reg"]
fn get_reg(cursor: *mut unw_cursor_t, reg: unw_regnum_t, val: *mut unw_word_t) -> c_int;
#[link_name = "_Uaarch64_step"]
fn step(cursor: *mut unw_cursor_t) -> c_int;
#[link_name = "_Uaarch64_get_proc_name"]
fn get_proc_name(
cursor: *mut unw_cursor,
buffer: *mut c_char,
len: size_t,
offset: *mut unw_word_t,
) -> c_int;
#[link_name = "_Uaarch64_set_caching_policy"]
fn set_caching_policy(spc: unw_addr_space_t, policy: unw_caching_policy_t) -> c_int;
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Error::UNW_EUNSPEC => write!(f, "UNW_EUNSPEC: unspecified (general) error"),
Error::UNW_ENOMEM => write!(f, "UNW_ENOMEM: out of memoryr"),
Error::UNW_EBADREG => write!(f, "UNW_EBADREG: bad register number"),
Error::UNW_EREADONLYREG => {
write!(f, "UNW_EREADONLYREG: attempt to write read-only register ")
}
Error::UNW_ESTOPUNWIND => write!(f, "UNW_ESTOPUNWIND: stop unwinding"),
Error::UNW_EINVALIDIP => write!(f, "UNW_EINVALIDIP: invalid IP"),
Error::UNW_EBADFRAME => write!(f, "UNW_EBADFRAME: bad frame"),
Error::UNW_EINVAL => write!(f, "UNW_EINVAL: unsupported operation or bad value"),
Error::UNW_EBADVERSION => {
write!(f, "UNW_EBADVERSION: unwind info has unsupported version")
}
Error::UNW_ENOINFO => write!(f, "UNW_ENOINFO: no unwind info found"),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
"LibunwindErrror"
}
fn cause(&self) -> Option<&dyn std::error::Error> {
None
}
}
impl Error {
fn from(ret: i32) -> Error {
match ret as u32 {
bindings::unw_error_t_UNW_EUNSPEC => Error::UNW_EUNSPEC,
bindings::unw_error_t_UNW_ENOMEM => Error::UNW_ENOMEM,
bindings::unw_error_t_UNW_EBADREG => Error::UNW_EBADREG,
bindings::unw_error_t_UNW_EREADONLYREG => Error::UNW_EREADONLYREG,
bindings::unw_error_t_UNW_ESTOPUNWIND => Error::UNW_ESTOPUNWIND,
bindings::unw_error_t_UNW_EINVALIDIP => Error::UNW_EINVALIDIP,
bindings::unw_error_t_UNW_EBADFRAME => Error::UNW_EBADFRAME,
bindings::unw_error_t_UNW_EINVAL => Error::UNW_EINVAL,
bindings::unw_error_t_UNW_EBADVERSION => Error::UNW_EBADVERSION,
bindings::unw_error_t_UNW_ENOINFO => Error::UNW_ENOINFO,
_ => Error::UNW_EUNSPEC,
}
}
}