]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys_common/gnu/libbacktrace.rs
Rollup merge of #39604 - est31:i128_tests, r=alexcrichton
[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
43     extern {
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;
59     }
60
61     ////////////////////////////////////////////////////////////////////////
62     // helper callbacks
63     ////////////////////////////////////////////////////////////////////////
64
65     type FileLine = (*const libc::c_char, libc::c_int);
66
67     extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
68                        _errnum: libc::c_int) {
69         // do nothing for now
70     }
71     extern fn syminfo_cb(data: *mut libc::c_void,
72                          _pc: libc::uintptr_t,
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; }
78     }
79     extern fn pcinfo_cb(data: *mut libc::c_void,
80                         _pc: libc::uintptr_t,
81                         filename: *const libc::c_char,
82                         lineno: libc::c_int,
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)};
87
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..]); }
93             }
94         }
95
96         0
97     }
98
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.
102     //
103     // I would love to register an at_exit() handler which cleans up this
104     // state, but libbacktrace provides no way to do so.
105     //
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).
109     //
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
113     // approach.
114     //
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 }
125
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);
133                 mem::forget(file);
134                 filename_ptr
135             },
136             Err(_) => ptr::null(),
137         };
138
139         STATE = backtrace_create_state(filename, 0, error_cb,
140                                        ptr::null_mut());
141         STATE
142     }
143
144     ////////////////////////////////////////////////////////////////////////
145     // translation
146     ////////////////////////////////////////////////////////////////////////
147
148     // backtrace errors are currently swept under the rug, only I/O
149     // errors are reported
150     let state = unsafe { init_state() };
151     if state.is_null() {
152         return output(w, idx, addr, None)
153     }
154     let mut data = ptr::null();
155     let data_addr = &mut data as *mut *const libc::c_char;
156     let ret = unsafe {
157         backtrace_syminfo(state, symaddr as libc::uintptr_t,
158                           syminfo_cb, error_cb,
159                           data_addr as *mut libc::c_void)
160     };
161     if ret == 0 || data.is_null() {
162         output(w, idx, addr, None)?;
163     } else {
164         output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))?;
165     }
166
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];
172     let ret;
173     let fileline_count;
174     {
175         let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
176         let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
177         ret = unsafe {
178             backtrace_pcinfo(state, addr as libc::uintptr_t,
179                              pcinfo_cb, error_cb,
180                              fileline_addr as *mut libc::c_void)
181         };
182         fileline_count = FILELINE_SIZE - fileline_win.len();
183     }
184     if ret == 0 {
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)?;
189         }
190     }
191
192     Ok(())
193 }