]>
Commit | Line | Data |
---|---|---|
7cac9316 XL |
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 | #![allow(bad_style)] | |
12 | ||
13 | extern crate backtrace_sys as bt; | |
14 | ||
15 | use libc::uintptr_t; | |
16 | use std::ffi::{CStr, OsStr}; | |
17 | use std::os::raw::{c_void, c_char, c_int}; | |
18 | use std::os::unix::prelude::*; | |
19 | use std::path::Path; | |
20 | use std::ptr; | |
21 | use std::sync::{ONCE_INIT, Once}; | |
22 | ||
23 | use SymbolName; | |
24 | ||
7cac9316 XL |
25 | pub enum Symbol { |
26 | Syminfo { | |
27 | pc: uintptr_t, | |
28 | symname: *const c_char, | |
29 | }, | |
30 | Pcinfo { | |
31 | pc: uintptr_t, | |
32 | filename: *const c_char, | |
33 | lineno: c_int, | |
34 | function: *const c_char, | |
35 | }, | |
36 | } | |
37 | ||
38 | impl Symbol { | |
39 | pub fn name(&self) -> Option<SymbolName> { | |
40 | let ptr = match *self { | |
41 | Symbol::Syminfo { symname, .. } => symname, | |
42 | Symbol::Pcinfo { function, .. } => function, | |
43 | }; | |
44 | if ptr.is_null() { | |
45 | None | |
46 | } else { | |
47 | Some(SymbolName::new(unsafe { CStr::from_ptr(ptr).to_bytes() })) | |
48 | } | |
49 | } | |
50 | ||
51 | pub fn addr(&self) -> Option<*mut c_void> { | |
52 | let pc = match *self { | |
53 | Symbol::Syminfo { pc, .. } => pc, | |
54 | Symbol::Pcinfo { pc, .. } => pc, | |
55 | }; | |
56 | if pc == 0 {None} else {Some(pc as *mut _)} | |
57 | } | |
58 | ||
59 | pub fn filename(&self) -> Option<&Path> { | |
60 | match *self { | |
61 | Symbol::Syminfo { .. } => None, | |
62 | Symbol::Pcinfo { filename, .. } => { | |
63 | Some(Path::new(OsStr::from_bytes(unsafe { | |
64 | CStr::from_ptr(filename).to_bytes() | |
65 | }))) | |
66 | } | |
67 | } | |
68 | } | |
69 | ||
70 | pub fn lineno(&self) -> Option<u32> { | |
71 | match *self { | |
72 | Symbol::Syminfo { .. } => None, | |
73 | Symbol::Pcinfo { lineno, .. } => Some(lineno as u32), | |
74 | } | |
75 | } | |
76 | } | |
77 | ||
78 | extern fn error_cb(_data: *mut c_void, _msg: *const c_char, | |
79 | _errnum: c_int) { | |
80 | // do nothing for now | |
81 | } | |
82 | ||
83 | extern fn syminfo_cb(data: *mut c_void, | |
84 | pc: uintptr_t, | |
85 | symname: *const c_char, | |
86 | _symval: uintptr_t, | |
87 | _symsize: uintptr_t) { | |
88 | unsafe { | |
89 | call(data, &super::Symbol { | |
90 | inner: Symbol::Syminfo { | |
91 | pc: pc, | |
92 | symname: symname, | |
93 | }, | |
94 | }); | |
95 | } | |
96 | } | |
97 | ||
98 | extern fn pcinfo_cb(data: *mut c_void, | |
99 | pc: uintptr_t, | |
100 | filename: *const c_char, | |
101 | lineno: c_int, | |
102 | function: *const c_char) -> c_int { | |
103 | unsafe { | |
104 | if filename.is_null() || function.is_null() { | |
105 | return -1 | |
106 | } | |
107 | call(data, &super::Symbol { | |
108 | inner: Symbol::Pcinfo { | |
109 | pc: pc, | |
110 | filename: filename, | |
111 | lineno: lineno, | |
112 | function: function, | |
113 | }, | |
114 | }); | |
115 | return 0 | |
116 | } | |
117 | } | |
118 | ||
119 | unsafe fn call(data: *mut c_void, sym: &super::Symbol) { | |
120 | let cb = data as *mut &mut FnMut(&super::Symbol); | |
121 | let mut bomb = ::Bomb { enabled: true }; | |
122 | (*cb)(sym); | |
123 | bomb.enabled = false; | |
124 | } | |
125 | ||
126 | // The libbacktrace API supports creating a state, but it does not | |
127 | // support destroying a state. I personally take this to mean that a | |
128 | // state is meant to be created and then live forever. | |
129 | // | |
130 | // I would love to register an at_exit() handler which cleans up this | |
131 | // state, but libbacktrace provides no way to do so. | |
132 | // | |
133 | // With these constraints, this function has a statically cached state | |
134 | // that is calculated the first time this is requested. Remember that | |
135 | // backtracing all happens serially (one global lock). | |
136 | // | |
137 | // Things don't work so well on not-Linux since libbacktrace can't track down | |
138 | // that executable this is. We at one point used env::current_exe but it turns | |
139 | // out that there are some serious security issues with that approach. | |
140 | // | |
141 | // Specifically, on certain platforms like BSDs, a malicious actor can cause an | |
142 | // arbitrary file to be placed at the path returned by current_exe. libbacktrace | |
143 | // does not behave defensively in the presence of ill-formed DWARF information, | |
144 | // and has been demonstrated to segfault in at least one case. There is no | |
145 | // evidence at the moment to suggest that a more carefully constructed file | |
146 | // can't cause arbitrary code execution. As a result of all of this, we don't | |
147 | // hint libbacktrace with the path to the current process. | |
148 | unsafe fn init_state() -> *mut bt::backtrace_state { | |
149 | static mut STATE: *mut bt::backtrace_state = 0 as *mut _; | |
150 | static INIT: Once = ONCE_INIT; | |
151 | INIT.call_once(|| { | |
152 | // Our libbacktrace may not have multithreading support, so | |
153 | // set `threaded = 0` and synchronize ourselves. | |
154 | STATE = bt::backtrace_create_state(ptr::null(), 0, error_cb, | |
155 | ptr::null_mut()); | |
156 | }); | |
157 | ||
158 | STATE | |
159 | } | |
160 | ||
161 | pub fn resolve(symaddr: *mut c_void, mut cb: &mut FnMut(&super::Symbol)) { | |
162 | let _guard = ::lock::lock(); | |
163 | ||
164 | // backtrace errors are currently swept under the rug | |
165 | unsafe { | |
166 | let state = init_state(); | |
167 | if state.is_null() { | |
168 | return | |
169 | } | |
170 | ||
171 | let ret = bt::backtrace_pcinfo(state, symaddr as uintptr_t, | |
172 | pcinfo_cb, error_cb, | |
173 | &mut cb as *mut _ as *mut _); | |
174 | if ret != 0 { | |
175 | bt::backtrace_syminfo(state, symaddr as uintptr_t, | |
176 | syminfo_cb, error_cb, | |
177 | &mut cb as *mut _ as *mut _); | |
178 | } | |
179 | } | |
180 | } |