libunwind_sys/
lib.rs

1//! Low-level bindings for the [libunwind] library.
2//!
3//! Please see the libunwind  [C API documentation] for function descriptions.
4//!
5//! [libunwind]: http://www.nongnu.org/libunwind/
6//! [C API documentation]: https://www.nongnu.org/libunwind/docs.html
7
8#![allow(non_upper_case_globals)]
9#![allow(non_camel_case_types)]
10#![allow(non_snake_case)]
11
12include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
13
14pub use crate::native::*;
15
16#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
17#[cfg_attr(target_arch = "x86", path = "x86.rs")]
18#[cfg_attr(target_arch = "arm", path = "arm.rs")]
19mod native;
20
21#[cfg(test)]
22mod tests {
23    extern crate libc;
24
25    use crate::*;
26    use libc::c_char;
27    use std::ffi::{CStr, CString};
28    use std::mem::MaybeUninit;
29    use std::path::PathBuf;
30
31    unsafe fn unwind_core_dump(core_file_name: &str) -> String {
32        let mut core_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
33        core_path_buf.push(format!("data/{}", core_file_name));
34
35        let core_path = CString::new(core_path_buf.to_str().unwrap()).unwrap();
36
37        let asp = unw_create_addr_space(&raw mut _UCD_accessors, 0);
38        let ui: *mut UCD_info = _UCD_create(core_path.as_ptr());
39        let mut c = MaybeUninit::uninit();
40        let _ret = unw_init_remote(c.as_mut_ptr(), asp, ui as *mut libc::c_void);
41
42        let mut ip: unw_word_t = 0;
43        let mut sp: unw_word_t = 0;
44        let mut val: unw_word_t = 0;
45        let mut backtrace = String::new();
46
47        loop {
48            unw_get_reg(
49                c.as_mut_ptr(),
50                UNW_TDEP_IP as ::std::os::raw::c_int,
51                &mut ip,
52            );
53            unw_get_reg(
54                c.as_mut_ptr(),
55                UNW_TDEP_SP as ::std::os::raw::c_int,
56                &mut sp,
57            );
58            let ret = _UCD_access_mem(asp, sp, &mut val, 0, ui as *mut libc::c_void);
59            if ret < 0 {
60                assert!(false);
61            }
62            let mut off = MaybeUninit::uninit();
63            let mut name_vec: Vec<c_char> = vec![0; 64];
64            unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(), 64, off.as_mut_ptr());
65            let name = CStr::from_ptr(name_vec.as_mut_ptr());
66            backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
67            let ret = unw_step(c.as_mut_ptr());
68            if ret <= 0 {
69                break;
70            }
71        }
72
73        backtrace
74    }
75
76    #[test]
77    #[cfg(target_arch = "x86_64")]
78    fn test_core_unwind() {
79        unsafe {
80            let backtrace = unwind_core_dump("core.test_callstack");
81            assert!(backtrace.contains("0x40054b"));
82            assert!(backtrace.contains("0x400527"));
83            assert!(backtrace.contains("0x4004fd"));
84            assert!(backtrace.contains("0x400579"));
85        }
86    }
87
88    #[test]
89    #[cfg(target_arch = "x86_64")]
90    fn test_core_unwind_heap_error() {
91        unsafe {
92            let backtrace = unwind_core_dump("core.test_heapError");
93            assert!(backtrace.contains("0x3031306566633262"));
94            assert!(backtrace.contains("0x7f90e060b37a"));
95        }
96    }
97
98    #[test]
99    #[cfg(target_arch = "x86_64")]
100    fn test_core_unwind_canary() {
101        unsafe {
102            let backtrace = unwind_core_dump("core.test_canary");
103            println!("{:?}", &backtrace);
104            assert!(backtrace.contains("0x7fc14b44f15c"));
105            assert!(backtrace.contains("0x7fc14b36b428"));
106        }
107    }
108
109    #[test]
110    #[cfg(target_arch = "x86_64")]
111    fn test_local_unwind() {
112        unsafe {
113            let mut c = MaybeUninit::uninit();
114            let mut uc = MaybeUninit::uninit();
115            let mut ip: unw_word_t = 0;
116            let _ret = unw_getcontext(uc.as_mut_ptr());
117            let _ret = unw_init_local(c.as_mut_ptr(), uc.as_mut_ptr());
118            let mut backtrace = String::new();
119            loop {
120                unw_get_reg(
121                    c.as_mut_ptr(),
122                    UNW_TDEP_IP as ::std::os::raw::c_int,
123                    &mut ip,
124                );
125                let mut off = MaybeUninit::uninit();
126                let mut name_vec: Vec<c_char> = vec![0; 64];
127                unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(), 64, off.as_mut_ptr());
128                let name = CStr::from_ptr(name_vec.as_mut_ptr());
129                backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
130                let ret = unw_step(c.as_mut_ptr());
131                if ret <= 0 {
132                    break;
133                }
134            }
135            assert!(backtrace.contains("test_local_unwind"));
136            assert!(backtrace.contains("start_thread") || backtrace.contains("start"));
137        }
138    }
139
140    #[test]
141    #[cfg(all(feature = "ptrace", target_arch = "x86_64"))]
142    fn test_remote_unwind() {
143        unsafe {
144            let mut c = MaybeUninit::uninit();
145            let mut ip: unw_word_t = 0;
146            let asp = unw_create_addr_space(&mut _UPT_accessors, 0);
147            //spawn child proccess
148            let mut test_callstack_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
149            test_callstack_path_buf.push("data/test_callstack_remote");
150            let mut child = Command::new(test_callstack_path_buf.to_str().unwrap())
151                .spawn()
152                .expect("failed to execute child");
153            thread::sleep(Duration::from_millis(10));
154            let ret = libc::ptrace(
155                libc::PTRACE_ATTACH,
156                child.id() as libc::pid_t,
157                ptr::null_mut::<c_void>(),
158                ptr::null_mut::<c_void>(),
159            );
160            if ret != 0 {
161                panic!("{}", io::Error::last_os_error());
162            }
163
164            loop {
165                let mut status = 0;
166                let ret = libc::waitpid(child.id() as libc::pid_t, &mut status, 0);
167                if ret < 0 {
168                    panic!("{}", io::Error::last_os_error());
169                }
170                if libc::WIFSTOPPED(status) {
171                    break;
172                }
173            }
174
175            let ui: *mut ::std::os::raw::c_void = _UPT_create(child.id() as i32);
176            let mut backtrace = String::new();
177
178            let _ret = unw_init_remote(c.as_mut_ptr(), asp, ui as *mut libc::c_void);
179            loop {
180                unw_get_reg(
181                    c.as_mut_ptr(),
182                    UNW_TDEP_IP as ::std::os::raw::c_int,
183                    &mut ip,
184                );
185                let mut off = MaybeUninit::uninit();
186                let mut name_vec: Vec<c_char> = vec![0; 64];
187                unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(), 64, off.as_mut_ptr());
188                let name = CStr::from_ptr(name_vec.as_mut_ptr());
189                backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
190                let ret = unw_step(c.as_mut_ptr());
191                if ret <= 0 {
192                    break;
193                }
194            }
195            assert!(backtrace.contains("main"));
196            assert!(backtrace.contains("first"));
197            assert!(backtrace.contains("second"));
198            assert!(backtrace.contains("third"));
199            _UPT_destroy(ui);
200            unw_destroy_addr_space(asp);
201            child.kill().unwrap();
202        }
203    }
204}