1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
14 use sys_common::backtrace::{output, output_fileline};
16 pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
17 symaddr: *mut libc::c_void) -> io::Result<()> {
22 ////////////////////////////////////////////////////////////////////////
24 ////////////////////////////////////////////////////////////////////////
25 type backtrace_syminfo_callback =
26 extern "C" fn(data: *mut libc::c_void,
28 symname: *const libc::c_char,
29 symval: libc::uintptr_t,
30 symsize: libc::uintptr_t);
31 type backtrace_full_callback =
32 extern "C" fn(data: *mut libc::c_void,
34 filename: *const libc::c_char,
36 function: *const libc::c_char) -> libc::c_int;
37 type backtrace_error_callback =
38 extern "C" fn(data: *mut libc::c_void,
39 msg: *const libc::c_char,
41 enum backtrace_state {}
42 #[link(name = "backtrace", kind = "static")]
43 #[cfg(all(not(test), not(cargobuild)))]
47 fn backtrace_create_state(filename: *const libc::c_char,
48 threaded: libc::c_int,
49 error: backtrace_error_callback,
50 data: *mut libc::c_void)
51 -> *mut backtrace_state;
52 fn backtrace_syminfo(state: *mut backtrace_state,
53 addr: libc::uintptr_t,
54 cb: backtrace_syminfo_callback,
55 error: backtrace_error_callback,
56 data: *mut libc::c_void) -> libc::c_int;
57 fn backtrace_pcinfo(state: *mut backtrace_state,
58 addr: libc::uintptr_t,
59 cb: backtrace_full_callback,
60 error: backtrace_error_callback,
61 data: *mut libc::c_void) -> libc::c_int;
64 ////////////////////////////////////////////////////////////////////////
66 ////////////////////////////////////////////////////////////////////////
68 type FileLine = (*const libc::c_char, libc::c_int);
70 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
71 _errnum: libc::c_int) {
74 extern fn syminfo_cb(data: *mut libc::c_void,
76 symname: *const libc::c_char,
77 _symval: libc::uintptr_t,
78 _symsize: libc::uintptr_t) {
79 let slot = data as *mut *const libc::c_char;
80 unsafe { *slot = symname; }
82 extern fn pcinfo_cb(data: *mut libc::c_void,
84 filename: *const libc::c_char,
86 _function: *const libc::c_char) -> libc::c_int {
87 if !filename.is_null() {
88 let slot = data as *mut &mut [FileLine];
89 let buffer = unsafe {ptr::read(slot)};
91 // if the buffer is not full, add file:line to the buffer
92 // and adjust the buffer for next possible calls to pcinfo_cb.
93 if !buffer.is_empty() {
94 buffer[0] = (filename, lineno);
95 unsafe { ptr::write(slot, &mut buffer[1..]); }
102 // The libbacktrace API supports creating a state, but it does not
103 // support destroying a state. I personally take this to mean that a
104 // state is meant to be created and then live forever.
106 // I would love to register an at_exit() handler which cleans up this
107 // state, but libbacktrace provides no way to do so.
109 // With these constraints, this function has a statically cached state
110 // that is calculated the first time this is requested. Remember that
111 // backtracing all happens serially (one global lock).
113 // Things don't work so well on not-Linux since libbacktrace can't track
114 // down that executable this is. We at one point used env::current_exe but
115 // it turns out that there are some serious security issues with that
118 // Specifically, on certain platforms like BSDs, a malicious actor can cause
119 // an arbitrary file to be placed at the path returned by current_exe.
120 // libbacktrace does not behave defensively in the presence of ill-formed
121 // DWARF information, and has been demonstrated to segfault in at least one
122 // case. There is no evidence at the moment to suggest that a more carefully
123 // constructed file can't cause arbitrary code execution. As a result of all
124 // of this, we don't hint libbacktrace with the path to the current process.
125 unsafe fn init_state() -> *mut backtrace_state {
126 static mut STATE: *mut backtrace_state = ptr::null_mut();
127 if !STATE.is_null() { return STATE }
129 let filename = match ::sys::backtrace::gnu::get_executable_filename() {
130 Ok((filename, file)) => {
131 // filename is purposely leaked here since libbacktrace requires
132 // it to stay allocated permanently, file is also leaked so that
133 // the file stays locked
134 let filename_ptr = filename.as_ptr();
135 mem::forget(filename);
139 Err(_) => ptr::null(),
142 STATE = backtrace_create_state(filename, 0, error_cb,
147 ////////////////////////////////////////////////////////////////////////
149 ////////////////////////////////////////////////////////////////////////
151 // backtrace errors are currently swept under the rug, only I/O
152 // errors are reported
153 let state = unsafe { init_state() };
155 return output(w, idx, addr, None)
157 let mut data = ptr::null();
158 let data_addr = &mut data as *mut *const libc::c_char;
160 backtrace_syminfo(state, symaddr as libc::uintptr_t,
161 syminfo_cb, error_cb,
162 data_addr as *mut libc::c_void)
164 if ret == 0 || data.is_null() {
165 output(w, idx, addr, None)?;
167 output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
170 // pcinfo may return an arbitrary number of file:line pairs,
171 // in the order of stack trace (i.e. inlined calls first).
172 // in order to avoid allocation, we stack-allocate a fixed size of entries.
173 const FILELINE_SIZE: usize = 32;
174 let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
178 let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
179 let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
181 backtrace_pcinfo(state, addr as libc::uintptr_t,
183 fileline_addr as *mut libc::c_void)
185 fileline_count = FILELINE_SIZE - fileline_win.len();
188 for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
189 if file.is_null() { continue; } // just to be sure
190 let file = unsafe { CStr::from_ptr(file).to_bytes() };
191 output_fileline(w, file, line, i == FILELINE_SIZE - 1)?;