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 {}
44 fn backtrace_create_state(filename: *const libc::c_char,
45 threaded: libc::c_int,
46 error: backtrace_error_callback,
47 data: *mut libc::c_void)
48 -> *mut backtrace_state;
49 fn backtrace_syminfo(state: *mut backtrace_state,
50 addr: libc::uintptr_t,
51 cb: backtrace_syminfo_callback,
52 error: backtrace_error_callback,
53 data: *mut libc::c_void) -> libc::c_int;
54 fn backtrace_pcinfo(state: *mut backtrace_state,
55 addr: libc::uintptr_t,
56 cb: backtrace_full_callback,
57 error: backtrace_error_callback,
58 data: *mut libc::c_void) -> libc::c_int;
61 ////////////////////////////////////////////////////////////////////////
63 ////////////////////////////////////////////////////////////////////////
65 type FileLine = (*const libc::c_char, libc::c_int);
67 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
68 _errnum: libc::c_int) {
71 extern fn syminfo_cb(data: *mut libc::c_void,
73 symname: *const libc::c_char,
74 _symval: libc::uintptr_t,
75 _symsize: libc::uintptr_t) {
76 let slot = data as *mut *const libc::c_char;
77 unsafe { *slot = symname; }
79 extern fn pcinfo_cb(data: *mut libc::c_void,
81 filename: *const libc::c_char,
83 _function: *const libc::c_char) -> libc::c_int {
84 if !filename.is_null() {
85 let slot = data as *mut &mut [FileLine];
86 let buffer = unsafe {ptr::read(slot)};
88 // if the buffer is not full, add file:line to the buffer
89 // and adjust the buffer for next possible calls to pcinfo_cb.
90 if !buffer.is_empty() {
91 buffer[0] = (filename, lineno);
92 unsafe { ptr::write(slot, &mut buffer[1..]); }
99 // The libbacktrace API supports creating a state, but it does not
100 // support destroying a state. I personally take this to mean that a
101 // state is meant to be created and then live forever.
103 // I would love to register an at_exit() handler which cleans up this
104 // state, but libbacktrace provides no way to do so.
106 // With these constraints, this function has a statically cached state
107 // that is calculated the first time this is requested. Remember that
108 // backtracing all happens serially (one global lock).
110 // Things don't work so well on not-Linux since libbacktrace can't track
111 // down that executable this is. We at one point used env::current_exe but
112 // it turns out that there are some serious security issues with that
115 // Specifically, on certain platforms like BSDs, a malicious actor can cause
116 // an arbitrary file to be placed at the path returned by current_exe.
117 // libbacktrace does not behave defensively in the presence of ill-formed
118 // DWARF information, and has been demonstrated to segfault in at least one
119 // case. There is no evidence at the moment to suggest that a more carefully
120 // constructed file can't cause arbitrary code execution. As a result of all
121 // of this, we don't hint libbacktrace with the path to the current process.
122 unsafe fn init_state() -> *mut backtrace_state {
123 static mut STATE: *mut backtrace_state = ptr::null_mut();
124 if !STATE.is_null() { return STATE }
126 let filename = match ::sys::backtrace::gnu::get_executable_filename() {
127 Ok((filename, file)) => {
128 // filename is purposely leaked here since libbacktrace requires
129 // it to stay allocated permanently, file is also leaked so that
130 // the file stays locked
131 let filename_ptr = filename.as_ptr();
132 mem::forget(filename);
136 Err(_) => ptr::null(),
139 STATE = backtrace_create_state(filename, 0, error_cb,
144 ////////////////////////////////////////////////////////////////////////
146 ////////////////////////////////////////////////////////////////////////
148 // backtrace errors are currently swept under the rug, only I/O
149 // errors are reported
150 let state = unsafe { init_state() };
152 return output(w, idx, addr, None)
154 let mut data = ptr::null();
155 let data_addr = &mut data as *mut *const libc::c_char;
157 backtrace_syminfo(state, symaddr as libc::uintptr_t,
158 syminfo_cb, error_cb,
159 data_addr as *mut libc::c_void)
161 if ret == 0 || data.is_null() {
162 output(w, idx, addr, None)?;
164 output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
167 // pcinfo may return an arbitrary number of file:line pairs,
168 // in the order of stack trace (i.e. inlined calls first).
169 // in order to avoid allocation, we stack-allocate a fixed size of entries.
170 const FILELINE_SIZE: usize = 32;
171 let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
175 let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
176 let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
178 backtrace_pcinfo(state, addr as libc::uintptr_t,
180 fileline_addr as *mut libc::c_void)
182 fileline_count = FILELINE_SIZE - fileline_win.len();
185 for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
186 if file.is_null() { continue; } // just to be sure
187 let file = unsafe { CStr::from_ptr(file).to_bytes() };
188 output_fileline(w, file, line, i == FILELINE_SIZE - 1)?;