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;
28    use std::ffi::CString;
29    use std::mem::MaybeUninit;
30    use std::path::PathBuf;
31    use std::process::Command;
32    use libc::c_void;
33    use std::ptr;
34    use std::thread;
35    use std::time::Duration;
36    use std::io;
37
38    #[test]
39    #[cfg(target_arch = "x86_64")]
40    fn test_core_unwind() {
41        unsafe {
42            let mut libc_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
43            libc_path_buf.push("data/libc-2.23.so");
44            let mut test_callstack_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
45            test_callstack_path_buf.push("data/test_callstack");
46            let mut core_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
47            core_path_buf.push("data/core.test_callstack");
48
49            let test_callstack_start:u64 = 0x400000;
50            let libc_start:u64 = 0x00007f9ac7468000;
51            let test_callstack_path = CString::new(test_callstack_path_buf.to_str().unwrap()).unwrap();
52            let libc_path = CString::new(libc_path_buf.to_str().unwrap()).unwrap();
53            let core_path = CString::new(core_path_buf.to_str().unwrap()).unwrap();
54
55            let asp = unw_create_addr_space(&mut _UCD_accessors ,0);
56            let ui: * mut UCD_info = _UCD_create(core_path.as_ptr());
57            let mut c  = MaybeUninit::uninit();
58            let _ret = unw_init_remote(c.as_mut_ptr(),asp,ui as * mut libc::c_void );
59            _UCD_add_backing_file_at_vaddr(ui, test_callstack_start, test_callstack_path.as_ptr());
60
61            _UCD_add_backing_file_at_vaddr(ui, libc_start, libc_path.as_ptr());
62           let mut ip: unw_word_t = 0;
63           let mut sp: unw_word_t = 0;
64           let mut val: unw_word_t = 0;
65           let mut backtrace = String::new();
66           loop {
67              unw_get_reg(c.as_mut_ptr(), UNW_TDEP_IP as ::std::os::raw::c_int, &mut ip);
68              unw_get_reg(c.as_mut_ptr(), UNW_TDEP_SP as ::std::os::raw::c_int, &mut sp);
69              let ret = _UCD_access_mem(asp, sp, &mut val, 0,ui as * mut libc::c_void);
70              if ret < 0 {
71                  assert!(false);
72              }
73              let mut off  = MaybeUninit::uninit();
74              let mut name_vec:Vec<c_char> = vec![0;64];
75              unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(),64, off.as_mut_ptr());
76              let name = CStr::from_ptr(name_vec.as_mut_ptr());
77              backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
78              let ret = unw_step(c.as_mut_ptr());
79              if ret <= 0 {
80                  break;
81              }
82           }
83           assert!(backtrace.contains("main"), true);
84           assert!(backtrace.contains("first"), true);
85           assert!(backtrace.contains("second"), true);
86           assert!(backtrace.contains("third"), true);
87        }
88    }
89    #[test]
90    #[cfg(target_arch = "x86_64")]
91    fn test_core_unwind_heap_error() {
92        unsafe {
93            let mut libc_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
94            libc_path_buf.push("data/libc-2.23.so");
95            let mut test_heap_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
96            test_heap_path_buf.push("data/test_heapError");
97            let mut core_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
98            core_path_buf.push("data/core.test_heapError");
99
100            let test_heap_start:u64 = 0x000055b7b218c000;
101            let libc_start:u64 = 0x00007f90e058b000;
102            let test_heap_path = CString::new(test_heap_path_buf.to_str().unwrap()).unwrap();
103            let libc_path = CString::new(libc_path_buf.to_str().unwrap()).unwrap();
104            let core_path = CString::new(core_path_buf.to_str().unwrap()).unwrap();
105            let asp = unw_create_addr_space(&mut _UCD_accessors ,0);
106            let ui: * mut UCD_info = _UCD_create(core_path.as_ptr());
107            let mut c  = MaybeUninit::uninit();
108            let _ret = unw_init_remote(c.as_mut_ptr(),asp,ui as * mut libc::c_void );
109            _UCD_add_backing_file_at_vaddr(ui, test_heap_start, test_heap_path.as_ptr());
110
111            _UCD_add_backing_file_at_vaddr(ui, libc_start, libc_path.as_ptr());
112           let mut ip: unw_word_t = 0;
113           let mut sp: unw_word_t = 0;
114           let mut val: unw_word_t = 0;
115           let mut backtrace = String::new();
116           loop {
117              unw_get_reg(c.as_mut_ptr(), UNW_TDEP_IP as ::std::os::raw::c_int, &mut ip);
118              unw_get_reg(c.as_mut_ptr(), UNW_TDEP_SP as ::std::os::raw::c_int, &mut sp);
119              let ret = _UCD_access_mem(asp, sp, &mut val, 0,ui as * mut libc::c_void);
120              if ret < 0 {
121                  assert!(false);
122              }
123              let mut off  = MaybeUninit::uninit();
124              let mut name_vec:Vec<c_char> = vec![0;64];
125              unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(),64, off.as_mut_ptr());
126              let name = CStr::from_ptr(name_vec.as_mut_ptr());
127              backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
128              let ret = unw_step(c.as_mut_ptr());
129              if ret <= 0 {
130                  break;
131              }
132           }
133           assert!(backtrace.contains("main"), true);
134           assert!(backtrace.contains("cfree"), true);
135        }
136    }
137    #[test]
138    #[cfg(target_arch = "x86_64")]
139    fn test_core_unwind_canary() {
140        unsafe {
141            let mut libc_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
142            libc_path_buf.push("data/libc-2.23.so");
143            let mut test_canary_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
144            test_canary_path_buf.push("data/test_canary");
145            let mut core_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
146            core_path_buf.push("data/core.test_canary");
147
148            let test_canary_start:u64 = 0x0000558672376000;
149            let libc_start:u64 = 0x00007fc14b336000;
150            let test_canary_path = CString::new(test_canary_path_buf.to_str().unwrap()).unwrap();
151            let libc_path = CString::new(libc_path_buf.to_str().unwrap()).unwrap();
152            let core_path = CString::new(core_path_buf.to_str().unwrap()).unwrap();
153            let asp = unw_create_addr_space(&mut _UCD_accessors ,0);
154            let ui: * mut UCD_info = _UCD_create(core_path.as_ptr());
155            let mut c  = MaybeUninit::uninit();
156            let _ret = unw_init_remote(c.as_mut_ptr(),asp,ui as * mut libc::c_void );
157            _UCD_add_backing_file_at_vaddr(ui, test_canary_start, test_canary_path.as_ptr());
158
159            _UCD_add_backing_file_at_vaddr(ui, libc_start, libc_path.as_ptr());
160           let mut ip: unw_word_t = 0;
161           let mut sp: unw_word_t = 0;
162           let mut val: unw_word_t = 0;
163           let mut backtrace = String::new();
164           loop {
165              unw_get_reg(c.as_mut_ptr(), UNW_TDEP_IP as ::std::os::raw::c_int, &mut ip);
166              unw_get_reg(c.as_mut_ptr(), UNW_TDEP_SP as ::std::os::raw::c_int, &mut sp);
167              let ret = _UCD_access_mem(asp, sp, &mut val, 0,ui as * mut libc::c_void);
168              if ret < 0 {
169                  assert!(false);
170              }
171              let mut off  = MaybeUninit::uninit();
172              let mut name_vec:Vec<c_char> = vec![0;64];
173              unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(),64, off.as_mut_ptr());
174              let name = CStr::from_ptr(name_vec.as_mut_ptr());
175              backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
176              let ret = unw_step(c.as_mut_ptr());
177              if ret <= 0 {
178                  break;
179              }
180           }
181           assert!(backtrace.contains("main"), true);
182           assert!(backtrace.contains("fortify_fail"), true);
183        }
184    }
185    #[test]
186    #[cfg(target_arch = "x86_64")]
187    fn test_local_unwind() {
188        unsafe {
189            let mut c  = MaybeUninit::uninit();
190            let mut uc  = MaybeUninit::uninit();
191            let mut ip: unw_word_t = 0;
192            let _ret = unw_getcontext(uc.as_mut_ptr());
193            let _ret = unw_init_local(c.as_mut_ptr(),uc.as_mut_ptr());
194            let mut backtrace = String::new();
195            loop {
196                unw_get_reg(c.as_mut_ptr(), UNW_TDEP_IP as ::std::os::raw::c_int, &mut ip);
197                let mut off  = MaybeUninit::uninit();
198                let mut name_vec:Vec<c_char> = vec![0;64];
199                unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(),64, off.as_mut_ptr());
200                let name = CStr::from_ptr(name_vec.as_mut_ptr());
201                backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
202                let ret = unw_step(c.as_mut_ptr());
203                if ret <= 0 {
204                    break;
205                }
206            }
207            println!("{}", backtrace);
208            assert!(backtrace.contains("test_local_unwind"), true);
209            assert!(backtrace.contains("start_thread") || backtrace.contains("start"), true);
210        }
211    }
212
213    #[test]
214    #[cfg(all(feature = "ptrace", target_arch = "x86_64"))]
215    fn test_remote_unwind() {
216        unsafe {
217            let mut c  = MaybeUninit::uninit();
218            let mut ip: unw_word_t = 0;
219            let asp = unw_create_addr_space(&mut _UPT_accessors ,0);
220            //spawn child proccess
221            let mut test_callstack_path_buf  = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
222            test_callstack_path_buf.push("data/test_callstack_remote");
223            let mut child = Command::new(test_callstack_path_buf.to_str().unwrap())
224                .spawn()
225                .expect("failed to execute child");
226             thread::sleep(Duration::from_millis(10));
227            let ret = libc::ptrace(
228                libc::PTRACE_ATTACH,
229                child.id() as libc::pid_t,
230                ptr::null_mut::<c_void>(),
231                ptr::null_mut::<c_void>(),
232            );
233            if ret != 0 {
234                panic!("{}", io::Error::last_os_error());
235            }
236
237            loop {
238                let mut status = 0;
239                let ret = libc::waitpid(child.id() as libc::pid_t, &mut status, 0);
240                if ret < 0 {
241                    panic!("{}", io::Error::last_os_error());
242                }
243                if libc::WIFSTOPPED(status) {
244                    break;
245                }
246            }
247
248            let ui: *mut ::std::os::raw::c_void = _UPT_create(child.id() as i32);
249            let mut backtrace = String::new();
250
251            let _ret = unw_init_remote(c.as_mut_ptr(),asp,ui as * mut libc::c_void );
252            loop {
253                unw_get_reg(c.as_mut_ptr(), UNW_TDEP_IP as ::std::os::raw::c_int, &mut ip);
254                let mut off  = MaybeUninit::uninit();
255                let mut name_vec:Vec<c_char> = vec![0;64];
256                unw_get_proc_name(c.as_mut_ptr(), name_vec.as_mut_ptr(),64, off.as_mut_ptr());
257                let name = CStr::from_ptr(name_vec.as_mut_ptr());
258                backtrace.push_str(&format!("0x{:x} in {:?} ()\n", ip, name.to_str().unwrap()));
259                let ret =  unw_step(c.as_mut_ptr());
260                if ret <= 0 {
261                    break;
262                }
263            }
264            assert!(backtrace.contains("main"), true);
265            assert!(backtrace.contains("first"), true);
266            assert!(backtrace.contains("second"), true);
267            assert!(backtrace.contains("third"), true);
268            _UPT_destroy(ui);
269            unw_destroy_addr_space(asp);
270            child.kill().unwrap();
271        }
272    }
273}