]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys_common/gnu/libbacktrace.rs
016c840d1541afbe4dd70d46d967c23461b23530
[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 libc;
12
13 use ffi::CStr;
14 use io;
15 use mem;
16 use ptr;
17 use sys::backtrace::BacktraceContext;
18 use sys_common::backtrace::Frame;
19
20 pub fn foreach_symbol_fileline<F>(frame: Frame,
21 mut f: F,
22 _: &BacktraceContext) -> io::Result<bool>
23 where F: FnMut(&[u8], libc::c_int) -> io::Result<()>
24 {
25 // pcinfo may return an arbitrary number of file:line pairs,
26 // in the order of stack trace (i.e. inlined calls first).
27 // in order to avoid allocation, we stack-allocate a fixed size of entries.
28 const FILELINE_SIZE: usize = 32;
29 let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE];
30 let ret;
31 let fileline_count = {
32 let state = unsafe { init_state() };
33 if state.is_null() {
34 return Err(io::Error::new(
35 io::ErrorKind::Other,
36 "failed to allocate libbacktrace state")
37 )
38 }
39 let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
40 let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
41 ret = unsafe {
42 backtrace_pcinfo(state,
43 frame.exact_position as libc::uintptr_t,
44 pcinfo_cb,
45 error_cb,
46 fileline_addr as *mut libc::c_void)
47 };
48 FILELINE_SIZE - fileline_win.len()
49 };
50 if ret == 0 {
51 for &(file, line) in &fileline_buf[..fileline_count] {
52 if file.is_null() { continue; } // just to be sure
53 let file = unsafe { CStr::from_ptr(file).to_bytes() };
54 f(file, line)?;
55 }
56 Ok(fileline_count == FILELINE_SIZE)
57 } else {
58 Ok(false)
59 }
60 }
61
62 /// Converts a pointer to symbol to its string value.
63 pub fn resolve_symname<F>(frame: Frame,
64 callback: F,
65 _: &BacktraceContext) -> io::Result<()>
66 where F: FnOnce(Option<&str>) -> io::Result<()>
67 {
68 let symname = {
69 let state = unsafe { init_state() };
70 if state.is_null() {
71 return Err(io::Error::new(
72 io::ErrorKind::Other,
73 "failed to allocate libbacktrace state")
74 )
75 }
76 let mut data = ptr::null();
77 let data_addr = &mut data as *mut *const libc::c_char;
78 let ret = unsafe {
79 backtrace_syminfo(state,
80 frame.symbol_addr as libc::uintptr_t,
81 syminfo_cb,
82 error_cb,
83 data_addr as *mut libc::c_void)
84 };
85 if ret == 0 || data.is_null() {
86 None
87 } else {
88 unsafe {
89 CStr::from_ptr(data).to_str().ok()
90 }
91 }
92 };
93 callback(symname)
94 }
95
96 ////////////////////////////////////////////////////////////////////////
97 // libbacktrace.h API
98 ////////////////////////////////////////////////////////////////////////
99 type backtrace_syminfo_callback =
100 extern "C" fn(data: *mut libc::c_void,
101 pc: libc::uintptr_t,
102 symname: *const libc::c_char,
103 symval: libc::uintptr_t,
104 symsize: libc::uintptr_t);
105 type backtrace_full_callback =
106 extern "C" fn(data: *mut libc::c_void,
107 pc: libc::uintptr_t,
108 filename: *const libc::c_char,
109 lineno: libc::c_int,
110 function: *const libc::c_char) -> libc::c_int;
111 type backtrace_error_callback =
112 extern "C" fn(data: *mut libc::c_void,
113 msg: *const libc::c_char,
114 errnum: libc::c_int);
115 enum backtrace_state {}
116
117 extern {
118 fn backtrace_create_state(filename: *const libc::c_char,
119 threaded: libc::c_int,
120 error: backtrace_error_callback,
121 data: *mut libc::c_void)
122 -> *mut backtrace_state;
123 fn backtrace_syminfo(state: *mut backtrace_state,
124 addr: libc::uintptr_t,
125 cb: backtrace_syminfo_callback,
126 error: backtrace_error_callback,
127 data: *mut libc::c_void) -> libc::c_int;
128 fn backtrace_pcinfo(state: *mut backtrace_state,
129 addr: libc::uintptr_t,
130 cb: backtrace_full_callback,
131 error: backtrace_error_callback,
132 data: *mut libc::c_void) -> libc::c_int;
133 }
134
135 ////////////////////////////////////////////////////////////////////////
136 // helper callbacks
137 ////////////////////////////////////////////////////////////////////////
138
139 type FileLine = (*const libc::c_char, libc::c_int);
140
141 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
142 _errnum: libc::c_int) {
143 // do nothing for now
144 }
145 extern fn syminfo_cb(data: *mut libc::c_void,
146 _pc: libc::uintptr_t,
147 symname: *const libc::c_char,
148 _symval: libc::uintptr_t,
149 _symsize: libc::uintptr_t) {
150 let slot = data as *mut *const libc::c_char;
151 unsafe { *slot = symname; }
152 }
153 extern fn pcinfo_cb(data: *mut libc::c_void,
154 _pc: libc::uintptr_t,
155 filename: *const libc::c_char,
156 lineno: libc::c_int,
157 _function: *const libc::c_char) -> libc::c_int {
158 if !filename.is_null() {
159 let slot = data as *mut &mut [FileLine];
160 let buffer = unsafe {ptr::read(slot)};
161
162 // if the buffer is not full, add file:line to the buffer
163 // and adjust the buffer for next possible calls to pcinfo_cb.
164 if !buffer.is_empty() {
165 buffer[0] = (filename, lineno);
166 unsafe { ptr::write(slot, &mut buffer[1..]); }
167 }
168 }
169
170 0
171 }
172
173 // The libbacktrace API supports creating a state, but it does not
174 // support destroying a state. I personally take this to mean that a
175 // state is meant to be created and then live forever.
176 //
177 // I would love to register an at_exit() handler which cleans up this
178 // state, but libbacktrace provides no way to do so.
179 //
180 // With these constraints, this function has a statically cached state
181 // that is calculated the first time this is requested. Remember that
182 // backtracing all happens serially (one global lock).
183 //
184 // Things don't work so well on not-Linux since libbacktrace can't track
185 // down that executable this is. We at one point used env::current_exe but
186 // it turns out that there are some serious security issues with that
187 // approach.
188 //
189 // Specifically, on certain platforms like BSDs, a malicious actor can cause
190 // an arbitrary file to be placed at the path returned by current_exe.
191 // libbacktrace does not behave defensively in the presence of ill-formed
192 // DWARF information, and has been demonstrated to segfault in at least one
193 // case. There is no evidence at the moment to suggest that a more carefully
194 // constructed file can't cause arbitrary code execution. As a result of all
195 // of this, we don't hint libbacktrace with the path to the current process.
196 unsafe fn init_state() -> *mut backtrace_state {
197 static mut STATE: *mut backtrace_state = ptr::null_mut();
198 if !STATE.is_null() { return STATE }
199
200 let filename = match ::sys::backtrace::gnu::get_executable_filename() {
201 Ok((filename, file)) => {
202 // filename is purposely leaked here since libbacktrace requires
203 // it to stay allocated permanently, file is also leaked so that
204 // the file stays locked
205 let filename_ptr = filename.as_ptr();
206 mem::forget(filename);
207 mem::forget(file);
208 filename_ptr
209 },
210 Err(_) => ptr::null(),
211 };
212
213 STATE = backtrace_create_state(filename, 0, error_cb,
214 ptr::null_mut());
215 STATE
216 }