]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys_common/gnu/libbacktrace.rs
Unignore u128 test for stage 0,1
[rust.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 mem;
20     use ptr;
21
22     ////////////////////////////////////////////////////////////////////////
23     // libbacktrace.h API
24     ////////////////////////////////////////////////////////////////////////
25     type backtrace_syminfo_callback =
26         extern "C" fn(data: *mut libc::c_void,
27                       pc: libc::uintptr_t,
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,
33                       pc: libc::uintptr_t,
34                       filename: *const libc::c_char,
35                       lineno: libc::c_int,
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,
40                       errnum: libc::c_int);
41     enum backtrace_state {}
42     #[link(name = "backtrace", kind = "static")]
43     #[cfg(all(not(test), not(cargobuild)))]
44     extern {}
45
46     extern {
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;
62     }
63
64     ////////////////////////////////////////////////////////////////////////
65     // helper callbacks
66     ////////////////////////////////////////////////////////////////////////
67
68     type FileLine = (*const libc::c_char, libc::c_int);
69
70     extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
71                        _errnum: libc::c_int) {
72         // do nothing for now
73     }
74     extern fn syminfo_cb(data: *mut libc::c_void,
75                          _pc: libc::uintptr_t,
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; }
81     }
82     extern fn pcinfo_cb(data: *mut libc::c_void,
83                         _pc: libc::uintptr_t,
84                         filename: *const libc::c_char,
85                         lineno: libc::c_int,
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)};
90
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..]); }
96             }
97         }
98
99         0
100     }
101
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.
105     //
106     // I would love to register an at_exit() handler which cleans up this
107     // state, but libbacktrace provides no way to do so.
108     //
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).
112     //
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
116     // approach.
117     //
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 }
128
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);
136                 mem::forget(file);
137                 filename_ptr
138             },
139             Err(_) => ptr::null(),
140         };
141
142         STATE = backtrace_create_state(filename, 0, error_cb,
143                                        ptr::null_mut());
144         STATE
145     }
146
147     ////////////////////////////////////////////////////////////////////////
148     // translation
149     ////////////////////////////////////////////////////////////////////////
150
151     // backtrace errors are currently swept under the rug, only I/O
152     // errors are reported
153     let state = unsafe { init_state() };
154     if state.is_null() {
155         return output(w, idx, addr, None)
156     }
157     let mut data = ptr::null();
158     let data_addr = &mut data as *mut *const libc::c_char;
159     let ret = unsafe {
160         backtrace_syminfo(state, symaddr as libc::uintptr_t,
161                           syminfo_cb, error_cb,
162                           data_addr as *mut libc::c_void)
163     };
164     if ret == 0 || data.is_null() {
165         output(w, idx, addr, None)?;
166     } else {
167         output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
168     }
169
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];
175     let ret;
176     let fileline_count;
177     {
178         let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
179         let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
180         ret = unsafe {
181             backtrace_pcinfo(state, addr as libc::uintptr_t,
182                              pcinfo_cb, error_cb,
183                              fileline_addr as *mut libc::c_void)
184         };
185         fileline_count = FILELINE_SIZE - fileline_win.len();
186     }
187     if ret == 0 {
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)?;
192         }
193     }
194
195     Ok(())
196 }