]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys/common/gnu/libbacktrace.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / sys / common / gnu / libbacktrace.rs
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.
4 //
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.
10
11 use io;
12 use io::prelude::*;
13 use libc;
14 use sys_common::backtrace::{output, output_fileline};
15
16 pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void,
17 symaddr: *mut libc::c_void) -> io::Result<()> {
18 use ffi::CStr;
19 use ptr;
20
21 ////////////////////////////////////////////////////////////////////////
22 // libbacktrace.h API
23 ////////////////////////////////////////////////////////////////////////
24 type backtrace_syminfo_callback =
25 extern "C" fn(data: *mut libc::c_void,
26 pc: libc::uintptr_t,
27 symname: *const libc::c_char,
28 symval: libc::uintptr_t,
29 symsize: libc::uintptr_t);
30 type backtrace_full_callback =
31 extern "C" fn(data: *mut libc::c_void,
32 pc: libc::uintptr_t,
33 filename: *const libc::c_char,
34 lineno: libc::c_int,
35 function: *const libc::c_char) -> libc::c_int;
36 type backtrace_error_callback =
37 extern "C" fn(data: *mut libc::c_void,
38 msg: *const libc::c_char,
39 errnum: libc::c_int);
40 enum backtrace_state {}
41 #[link(name = "backtrace", kind = "static")]
42 #[cfg(all(not(test), not(cargobuild)))]
43 extern {}
44
45 extern {
46 fn backtrace_create_state(filename: *const libc::c_char,
47 threaded: libc::c_int,
48 error: backtrace_error_callback,
49 data: *mut libc::c_void)
50 -> *mut backtrace_state;
51 fn backtrace_syminfo(state: *mut backtrace_state,
52 addr: libc::uintptr_t,
53 cb: backtrace_syminfo_callback,
54 error: backtrace_error_callback,
55 data: *mut libc::c_void) -> libc::c_int;
56 fn backtrace_pcinfo(state: *mut backtrace_state,
57 addr: libc::uintptr_t,
58 cb: backtrace_full_callback,
59 error: backtrace_error_callback,
60 data: *mut libc::c_void) -> libc::c_int;
61 }
62
63 ////////////////////////////////////////////////////////////////////////
64 // helper callbacks
65 ////////////////////////////////////////////////////////////////////////
66
67 type FileLine = (*const libc::c_char, libc::c_int);
68
69 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
70 _errnum: libc::c_int) {
71 // do nothing for now
72 }
73 extern fn syminfo_cb(data: *mut libc::c_void,
74 _pc: libc::uintptr_t,
75 symname: *const libc::c_char,
76 _symval: libc::uintptr_t,
77 _symsize: libc::uintptr_t) {
78 let slot = data as *mut *const libc::c_char;
79 unsafe { *slot = symname; }
80 }
81 extern fn pcinfo_cb(data: *mut libc::c_void,
82 _pc: libc::uintptr_t,
83 filename: *const libc::c_char,
84 lineno: libc::c_int,
85 _function: *const libc::c_char) -> libc::c_int {
86 if !filename.is_null() {
87 let slot = data as *mut &mut [FileLine];
88 let buffer = unsafe {ptr::read(slot)};
89
90 // if the buffer is not full, add file:line to the buffer
91 // and adjust the buffer for next possible calls to pcinfo_cb.
92 if !buffer.is_empty() {
93 buffer[0] = (filename, lineno);
94 unsafe { ptr::write(slot, &mut buffer[1..]); }
95 }
96 }
97
98 0
99 }
100
101 // The libbacktrace API supports creating a state, but it does not
102 // support destroying a state. I personally take this to mean that a
103 // state is meant to be created and then live forever.
104 //
105 // I would love to register an at_exit() handler which cleans up this
106 // state, but libbacktrace provides no way to do so.
107 //
108 // With these constraints, this function has a statically cached state
109 // that is calculated the first time this is requested. Remember that
110 // backtracing all happens serially (one global lock).
111 //
112 // Things don't work so well on not-Linux since libbacktrace can't track
113 // down that executable this is. We at one point used env::current_exe but
114 // it turns out that there are some serious security issues with that
115 // approach.
116 //
117 // Specifically, on certain platforms like BSDs, a malicious actor can cause
118 // an arbitrary file to be placed at the path returned by current_exe.
119 // libbacktrace does not behave defensively in the presence of ill-formed
120 // DWARF information, and has been demonstrated to segfault in at least one
121 // case. There is no evidence at the moment to suggest that a more carefully
122 // constructed file can't cause arbitrary code execution. As a result of all
123 // of this, we don't hint libbacktrace with the path to the current process.
124 unsafe fn init_state() -> *mut backtrace_state {
125 static mut STATE: *mut backtrace_state = ptr::null_mut();
126 if !STATE.is_null() { return STATE }
127 STATE = backtrace_create_state(ptr::null(), 0, error_cb,
128 ptr::null_mut());
129 STATE
130 }
131
132 ////////////////////////////////////////////////////////////////////////
133 // translation
134 ////////////////////////////////////////////////////////////////////////
135
136 // backtrace errors are currently swept under the rug, only I/O
137 // errors are reported
138 let state = unsafe { init_state() };
139 if state.is_null() {
140 return output(w, idx, addr, None)
141 }
142 let mut data = ptr::null();
143 let data_addr = &mut data as *mut *const libc::c_char;
144 let ret = unsafe {
145 backtrace_syminfo(state, symaddr as libc::uintptr_t,
146 syminfo_cb, error_cb,
147 data_addr as *mut libc::c_void)
148 };
149 if ret == 0 || data.is_null() {
150 output(w, idx, addr, None)?;
151 } else {
152 output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
153 }
154
155 // pcinfo may return an arbitrary number of file:line pairs,
156 // in the order of stack trace (i.e. inlined calls first).
157 // in order to avoid allocation, we stack-allocate a fixed size of entries.
158 const FILELINE_SIZE: usize = 32;
159 let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
160 let ret;
161 let fileline_count;
162 {
163 let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
164 let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
165 ret = unsafe {
166 backtrace_pcinfo(state, addr as libc::uintptr_t,
167 pcinfo_cb, error_cb,
168 fileline_addr as *mut libc::c_void)
169 };
170 fileline_count = FILELINE_SIZE - fileline_win.len();
171 }
172 if ret == 0 {
173 for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() {
174 if file.is_null() { continue; } // just to be sure
175 let file = unsafe { CStr::from_ptr(file).to_bytes() };
176 output_fileline(w, file, line, i == FILELINE_SIZE - 1)?;
177 }
178 }
179
180 Ok(())
181 }